Vue学习:19-插槽

在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段,为此 vue 提供了组件的插槽。

一、什么是插槽

在封装组件时,可以使用<slot>元素把不确定的、希望由用户指定的部分定义为插槽;插槽可以理解为给预留的内容提供占位符。

插槽也可以提供默认内容,如果组件的使用者没有为插槽提供任何内容,则插槽内的默认内容会生效。

注意:如果在封装组件时没有预留任何<slot>插槽,用户提供传递一些模板片段将会被遗弃。

二、插槽实例

Card.vue

<script setup>
</script>

<template>
  <div class="card">
    <h2 class="title"></h2>
    <div class="subtitle"></div>
    <div class="content"></div>
    <div class="action">
      <!-- 定义一个插槽 -->
      <!-- 插槽的默认内容,只有没有提供内容时,才会显示 -->
      <slot>卡片功能区域</slot>
    </div>
  </div>
</template>

<style>
.card {
  width: 250px;
  border: 1px solid black;
}
.card h2,
.card div {
  margin: 10px;
  padding: 5px;
}
.title {
  border: 1px solid red;
}
.subtitle {
  border: 1px solid green;
}
.content {
  border: 1px solid blue;
}
.action {
  border: 1px solid pink;
}
</style>

App.vue

<script setup>
  import CardVue from './components/Card.vue'
</script>

<!-- 视图区域(view) -->
<template>
  <CardVue>
    <!-- 向子组件插槽中提供内容 -->
    <button>关闭</button>
  </CardVue>

</template>

三、具名插槽

1、如果在封装组件时需要预留多个插槽节点,则需要为每个<slot>插槽指定具体的name名称,这种带有具体名称的插槽叫做“具名插槽”

2、没有指定name名称的插槽,会有隐含的名称叫做 default

3、在"<template>"元素上使用 v-slot:slotName或者#slotName向指定的具名插槽提供内容

实例:

Card.vue

<script setup>
</script>

<template>
  <div class="card">
    <h2 class="title">
      <!-- 带有name属性的插槽,称为具名插槽 -->
      <slot name="cardTitle"></slot>
    </h2>
    <div class="subtitle">
      <slot name="cardSubtitle"></slot>
    </div>
    <div class="content">
      <slot name="cardContent"></slot>
    </div>
    <div class="action">
      <!-- 定义一个插槽 -->
      <!-- 插槽的默认内容,只有没有提供内容时,才会显示 -->
      <!-- 没有name属性的插槽称为默认插槽,会有一个隐含的名字:default -->
      <slot>卡片功能区域</slot>
    </div>
  </div>
</template>

<style>
.card {
  width: 250px;
  border: 1px solid black;
}
.card h2,
.card div {
  margin: 10px;
  padding: 5px;
}
.title {
  border: 1px solid red;
}
.subtitle {
  border: 1px solid green;
}
.content {
  border: 1px solid blue;
}
.action {
  border: 1px solid pink;
}
</style>

App.vue

<script setup>
  import CardVue from './components/Card.vue'
</script>

<!-- 视图区域(view) -->
<template>
  <CardVue>
    <!-- 向具名插槽提供内容 -->
    <template v-slot:cardTitle>
      博客
    </template>

    <template #cardSubtitle>
      <i>百万博主分享经验</i>
    </template>

    <!-- 向默认插槽提供内容 -->
    <template #default>
      <button>关闭</button>
    </template>
  </CardVue>
</template>

四、作用域插槽

如何在向插槽提供的内容时获得子组件域内的数据呢?

1、在声明插槽时使用属性值的方式来传递子组件的数据,这种带有数据的插槽称之为作用域插槽

2、在"<template>"元素上使用v-slot:slotName="slotProps"或#slotName="slotProps"的方式来访问插槽传递属性值

3、如果没有使用"<template>"元素,而是直接在使用子组件中直接给默认插槽提供内容,我们可以在使用该子组件时用v-slot="slotProps"来接收该插槽传递的数据对象

注意:插槽上的name是一个Vue特别保留的属性,不会在作用域插槽中访问到

Card.vue

<script setup>
  import { reactive,ref } from 'vue';

  let blog = reactive({
    title: 'Java的选择结构',
    time: '2023-07-09 12:20:30'
  })

  let author = ref('爱学习的人')

  let closeBth = ref('关闭')
  let determine = ref('确定')

</script>

<template>
  <div class="card">
    <h2 class="title">
      <!-- 带有name属性的插槽,称为具名插槽 -->
      <slot name="cardTitle"></slot>
    </h2>
    <div class="subtitle">
      <slot name="cardSubtitle"></slot>
    </div>
    <div class="content">
      <!-- 带有数据的插槽称之为作用域插槽 -->
      <!-- <slot>插槽上的name是一个Vue特别保留的属性,不会在作用域插槽中访问到 -->
      <slot name="cardContent" :cardBlog="blog" :cardAuthor="author"></slot>
    </div>
    <div class="action">
      <!-- 定义一个插槽 -->
      <!-- 插槽的默认内容,只有没有提供内容时,才会显示 -->
      <!-- 没有name属性的插槽称为默认插槽,会有一个隐含的名字:default -->
      <slot :close="closeBth" :sure="determine">卡片功能区域</slot>
    </div>
  </div>
</template>

<style>
.card {
  width: 250px;
  border: 1px solid black;
}
.card h2,
.card div {
  margin: 10px;
  padding: 5px;
}
.title {
  border: 1px solid red;
}
.subtitle {
  border: 1px solid green;
}
.content {
  border: 1px solid blue;
}
.action {
  border: 1px solid pink;
}
</style>

App.vue

<script setup>
  import CardVue from './components/Card.vue'
</script>

<!-- 视图区域(view) -->
<template>
  <CardVue>
    <!-- 向具名插槽提供内容 -->
    <template v-slot:cardTitle>
      博客
    </template>

    <template #cardSubtitle>
      <i>百万博主分享经验</i>
    </template>

    <!-- 访问插槽中提供的数据 -->
    <template #cardContent="dataProps">
      <ul>
        <li>{{ dataProps }}</li>
        <li>博客的标题:{{ dataProps.cardBlog.title }}</li>
        <li>博客的时间:{{ dataProps.cardBlog.time }}</li>
        <li>博主:{{ dataProps.cardAuthor }}</li>
      </ul>
    </template>

    <!-- 向默认插槽提供内容 -->
    <template #default>
      <button>关闭</button>
    </template>
  </CardVue>
  <hr>
  <CardVue v-slot="dataProps">
    <!-- 如果使用子组件时用到了v-slot,则该子组件标签中将无法向其他具名插槽中提供内容 -->
    <!-- <template #cardSubtitle>
      <i>百万博主分享经验</i>
    </template> -->
    <button>{{ dataProps.close }}</button>
    <button>{{ dataProps.sure }}</button>
  </CardVue>
</template>

Failed to compile with 1 error 08:58:37 error in ./node_modules/md-editor-v3/node_modules/lru-cache/dist/esm/index.js Syntax Error: D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\md-editor-v3\node_modules\lru-cache\dist\esm\index.js: Class private methods are not enabled. Please add `@babel/plugin-transform-private-methods` to your configuration. 430 | return this.#keyMap.has(key) ? Infinity : 0; 431 | } > 432 | #initializeTTLTracking() { | ^ 433 | const ttls = new ZeroArray(this.#max); 434 | const starts = new ZeroArray(this.#max); 435 | this.#ttls = ttls; at transformFile.next (<anonymous>) at run.next (<anonymous>) at transform.next (<anonymous>) ERROR in ./src/views/document/docEdit.vue Module Error (from ./node_modules/vue-loader/dist/index.js): At least one <template> or <script> is required in a single file component. D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\src\views\document\docEdit.vue @ ./src/ lazy ^\.\/.*\/.*\.vue$ namespace object ./views/document/docEdit.vue @ ./src/store/user.ts 66:52-100 @ ./src/permission.ts 5:0-44 22:20-32 @ ./src/main.ts 6:0-34 29:10-16 ERROR in ./node_modules/md-editor-v3/node_modules/lru-cache/dist/esm/index.js Module build failed (from ./node_modules/babel-loader/lib/index.js): SyntaxError: D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\md-editor-v3\node_modules\lru-cache\dist\esm\index.js: Class private methods are not enabled. Please add `@babel/plugin-transform-private-methods` to your configuration. 430 | return this.#keyMap.has(key) ? Infinity : 0; 431 | } > 432 | #initializeTTLTracking() { | ^ 433 | const ttls = new ZeroArray(this.#max); 434 | const starts = new ZeroArray(this.#max); 435 | this.#ttls = ttls; at File.buildCodeFrameError (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\core\lib\transformation\file\file.js:200:12) at NodePath.buildCodeFrameError (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\path\index.js:118:21) at shouldTransform (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\helper-create-class-features-plugin\lib\features.js:130:29) at PluginPass.Class (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\helper-create-class-features-plugin\lib\index.js:110:44) at newFn (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\visitors.js:172:14) at NodePath._call (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\path\context.js:49:20) at NodePath.call (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\path\context.js:39:18) at NodePath.visit (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\path\context.js:88:31) at TraversalContext.visitQueue (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\context.js:90:16) at TraversalContext.visitSingle (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\context.js:66:19) at TraversalContext.visit (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\context.js:113:19) at traverseNode (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\traverse-node.js:131:17) at NodePath.visit (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\path\context.js:94:52) at TraversalContext.visitQueue (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\context.js:90:16) at TraversalContext.visitMultiple (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\context.js:62:17) at TraversalContext.visit (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\context.js:111:19) at traverseNode (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\traverse-node.js:131:17) at NodePath.visit (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\path\context.js:94:52) at TraversalContext.visitQueue (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\context.js:90:16) at TraversalContext.visitSingle (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\context.js:66:19) at TraversalContext.visit (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\context.js:113:19) at traverseNode (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\traverse-node.js:131:17) at traverse (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\traverse\lib\index.js:53:34) at transformFile (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\core\lib\transformation\index.js:80:31) at transformFile.next (<anonymous>) at run (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\core\lib\transformation\index.js:25:12) at run.next (<anonymous>) at transform (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\@babel\core\lib\transform.js:22:33) at transform.next (<anonymous>) at step (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\gensync\index.js:261:32) at D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\gensync\index.js:273:13 at async.call.result.err.err (D:\CCJ\SouceCode\Exercise Code\Exercise\2025\07\vue3-v6-OK\cqaos-web-v1\node_modules\gensync\index.js:223:11) @ ./node_modules/md-editor-v3/lib/es/chunks/index3.mjs 18:0-37 140:25-33 @ ./node_modules/md-editor-v3/lib/es/index.mjs 23:0-51 1336:14-21 1337:40-47 24:0-46 1343:0-315 @ ./src/main.ts 12:0-40 19:10-18 webpack compiled with 2 errors有爆粗
08-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值