vue插槽slot、slot-scope、v-slot的区别以及嵌套插槽的使用

本文详细解释了Vue中的slot、slot-scope和v-slot概念,包括它们的作用、在不同版本中的使用以及在实际开发中的应用,如ElementUItable组件中的插槽用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可以将 slot 理解成一个占位的东西,我们提前在模板中放置一个占位的标记,当其他地方要使用模板时就可以通过标记再传进来具体的内容,这样就可以实现更强的复用能力。

slot

slot 在英语中作为名词时表示:(可投入东西的)狭长孔,狭槽;(在一系列事件中为某事安排的)时间,空档;(组织、团体等中的)位置,职位。

在 vue 中翻译成插槽:vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 slot 元素作为承载分发内容的出口。

slot 在 vue 1.x 和 2.x 版本中都是支持的,但 vue 3 中已经被官方废弃了。

slot-scope

scope 英语翻译成:范围,领域。slot-scope 也就是插槽作用域,你可能会有疑问为什么会用这么个东西?

在 vue 中父组件通过 slot 传入子组件时,父组件中的 slot 里的内容只能访问父组件作用域里的数据(父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的),但是此时如果我们又想访问子组件中的数据怎么办呢?这就要用到 slot-scope 这个家伙了。

印象最深的应该就是 element-ui 中的 table 组件了,在渲染表格行时我们经常需要用到 slot-scope 来获取当前行的数据,其实这里就是我们上面说到的场景:

 
 
 
 
<template>
  <el-table :data="tableData">
    <el-table-column label="序号">
      <template slot-scope="scope">
        <span>{{ scope.$index + 1 }}</span>
      </template>
    </el-table-column>
    <el-table-column label="姓名">
      <template slot-scope="scope">
        <span>{{ scope.row.name }}</span>
      </template>
    </el-table-column>
    <el-table-column label="年龄">
      <!-- 支持直接通过 {} 去解构数据 -->
      <template slot-scope="{row}">
        <span>{{ row.age }}</span>
      </template>
    </el-table-column>
  </el-table>
</template>

slot-scope 主要也是配合 slot 一块使用,在 2.x 版本中都是支持的,但 vue 3 中已经被官方废弃了。

v-slot

vue 2.6.0 中引入,为具名插槽和作用域插槽提供新的统一的语法 v-slot 指令,用来代替 slot 和 slot-scope,所以如果 vue 使用的是 2.6 之后的版本就推荐直接使用 v-slot 了。

30af683eb4289095ee64023326ebe851.jpeg

单个插槽

单个插槽最简单,一般适用于比较简单的单个自定义内容渲染,子组件:

<div class="son">
  <h3>我是子组件</h3>
  <slot>我是默认内容,如果爸爸你不给我传东西我就显示这里的内容</slot>
</div>
<script>
  export default {
    name: 'Child'
  }
</script>
父组件:
<div class="father">
  <h3>我是父组件</h3>
  <Child>
    <p>儿子,我是爸爸给你传的内容</p>
  </Child>
</div>
<script>
  import Child from './Child.vue'
  export default {
    components: {
      Child
    }
  }
</script>

具名插槽

有时我们需要在子组件中提前定义多个占位符,这样就需要为每个占位符起一个名字,然后父组件中就可以直接通过这个名字来找到自己的位置了。也就是多个 slot 时,我们就需要给每个 slot 加一个 name 名字,允许有一个不加名字(其实如果不加他有一个默认的 default 名字,父组件中没有被其他 slot 包含的内容都会被当成这个没有名字的 default 里的内容)。子组件:

 
 
 
 
<div class="son">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot name="main"></slot>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
<script>
  export default {
    name: 'Child'
  }
</script>

父组件:

 
 
 
 
<div class="father">
  <h3>我是父组件</h3>
  <Child>
    <template slot="header">
      <h1>我是 header</h1>
    </template>


    <template v-slot:main>
      <p>我是 main</p>
    </template>


    <template v-slot:default>
      <p>A paragraph for the main content.</p>
    </template>


    <template #footer>
      <p>我是 footer</p>
    </template>
  </Child>
</div>
<script>
  import Child from './Child.vue'
  export default {
    components: {
      Child
    }
  }
</script>

可以直接通过 slot="xx" 去找到子组件中自己占位符,不过推荐用最新的 v-solt 指令,而且还可以通过 # 进一步来简写,就相当与 @ 代替 v-on,冒号 : 代替 v-bind 一样。没有名字的默认插槽可以通过 slot="default" 或者 v-slot:default 去找到自己的位置,或者啥也不写 Child 标签中只要没有被其他 slot 包裹的内容都是默认插槽的。

作用域插槽

也就是我们上面说到的 slot-scope,只不过新旧语法上有点差异,在子组件中直接在 slot 标签上绑定上数据属性:

 
 
 
 
<div class="son">
  <h3>我是子组件</h3>
  <slot :user="user"></slot>
</div>
<script>
  export default {
    name: 'Child',
    data() {
      return {
        user: {
          name: 'zhou',
          age: 18
        }
      }
    }
  }
</script>

父组件:

 
 
 
 
<div class="father">
  <h3>我是父组件</h3>
  <!-- 旧语法,2.6 之后不推荐使用了-->
  <Child>
    <template slot-scope="scope">
      <p>姓名:{{ scope.user.name }}</p>
      <p>年龄:{{ scope.user.age }}</p>
    </template>
  </Child>


  <!-- 新语法-->
  <Child>
    <template v-slot="scope">
      <p>姓名:{{ scope.user.name }}</p>
      <p>年龄:{{ scope.user.age }}</p>
    </template>
  </Child>
</div>
<script>
  import Child from './Child.vue'
  export default {
    components: {
      Child
    }
  }
</script>

嵌套作用域插槽

一般用于第三方组件的二次封装,我们需要在一个作用域插槽中再嵌套一个作用域插槽,比如在 element-ui 的 table 组件上二次封装:

 
 
 
 
<div class="son">
  <el-table :data="tableData">
    <el-table-column label="序号">
      <template slot-scope="scope">
        <span>{{ scope.$index + 1 }}</span>
      </template>
    </el-table-column>
    <el-table-column v-if="$slots.action" label="操作">
      <template slot-scope="{row}">
        <slot name="action" :row="row" />
      </template>
    </el-table-column>
  </el-table>
</div>
<script>
  export default {
    name: 'Child',
    data() {
      return {
        tableData: []
      }
    }
  }
</script>

$slots 用来访问被插槽分发的内容,每个具名插槽有其相应的 property (例如:v-slot:foo 中的内容将会在 vm.$slots.foo 中被找到),所以上面我们可以通过 $slots.action 来判断父组件中有这个插槽时才渲染。

父组件:

 
 
 
 
<div class="father">
  <h3>我是父组件</h3>
  <Child>
    <template slot="action" slot-scope="{ row }">
      <span @click="handleEdit(row)">编辑</span>
    </template>
  </Child>
</div>
<script>
  import Child from './Child.vue'
  export default {
    components: {
      Child
    },
    methods: {
      handleEdit(row) {
        console.log(row)
      }
    }
  }
</script>

注意上面父组件中我们并没有用推荐的最新的 v-slot 指令,是因为我项目中用到的 element-ui 版本是 2.13.2,刚开始想用 v-slot 发现一直没效果,最终翻源码才发现 element-ui 2.13.2 版本中的 vue 是 2.5 的,而 v-slot 是 vue 2.6 中才引入的,所以如果想在插槽中使用最新的 v-slot 指令,记得看下 vue 的版本哟。

### Vue3 中 `<slot>` 的使用方法及其与 Vue2 的区别 #### 一、Vue3 中 `<slot>` 的基本用法 在 Vue3 中,`<slot>` 是用于定义组件内部可被外部替换的内容区域的关键字。它支持多种类型的插槽,包括默认插槽、具名插槽和作用域插槽。 1. **默认插槽** 默认插槽是最简单的形式,当父组件传递内容到子组件时,默认会填充到未命名的 `<slot>` 中。 ```vue <!-- 子组件 --> <template> <div> <slot></slot> <!-- 默认插槽 --> </div> </template> <!-- 父组件 --> <ChildComponent> 这里是默认插槽的内容 </ChildComponent> ``` 2. **具名插槽** 具名插槽允许通过 `name` 属性指定多个不同的插槽位置。父组件可以通过 `v-slot:xxx` 或缩写 `#xxx` 来绑定具体插槽。 ```vue <!-- 子组件 --> <template> <div> <slot name="header"></slot> <!-- 命名插槽 header --> <slot></slot> <!-- 默认插槽 --> <slot name="footer"></slot><!-- 命名插槽 footer --> </div> </template> <!-- 父组件 --> <ChildComponent> <template #header>这里是头部</template> <p>这里是默认插槽的内容</p> <template #footer>这里是页脚</template> </ChildComponent> ``` 3. **作用域插槽** 作用域插槽允许子组件向父组件暴露数据,并让父组件决定如何渲染这些数据。这通常通过将数据作为参数传递给插槽实现。 ```vue <!-- 子组件 --> <script lang="ts" setup> import { ref } from 'vue' const obj = ref({ name: '作用域插槽', age: 23 }) </script> <template> <div> <slot name="footer" :obj="obj"></slot> <!-- 提供数据 obj 给父组件 --> </div> </template> <!-- 父组件 --> <ChildComponent> <template #footer="{ obj }"> 名称:{{ obj.name }},年龄:{{ obj.age }} </template> </ChildComponent> ``` #### 二、Vue3 和 Vue2 的主要区别 1. **语法简化** 在 Vue2 中,作用域插槽需要通过 `scope` 或 `slot-scope` 显式声明变量范围;而在 Vue3 中统一改为了 `v-slot` 指令[^3]。这种改变使得代码更加一致性和简洁。 - Vue2 示例: ```vue <child-component> <template slot="footer" scope="props"> <!-- 使用 slot-scope 定义作用域 --> {{ props.obj.name }} </template> </child-component> ``` - Vue3 示例: ```vue <child-component> <template v-slot:footer="{ obj }"> <!-- 使用 v-slot 替代 slot-scope --> {{ obj.name }} </template> </child-component> ``` 2. **动态插槽名称的支持** Vue3 支持动态插槽名称,而 Vue2 不完全支持这一特性。这意味着可以在运行时计算插槽名称并将其应用到模板中。 ```vue <child-component> <template #[dynamicSlotName]="{ data }"> 动态插槽内容:{{ data }} </template> </child-component> ``` 3. **性能优化** Vue3 对插槽进行了底层重写,提升了渲染效率和内存占用表现。特别是对于复杂嵌套场景下的插槽渲染,Vue3 更加高效[^4]。 4. **废弃的功能** Vue3 移除了对旧版 `scopedSlots` API 的支持,推荐开发者全面迁移到基于 `v-slot` 的新方式。此外,在 Vue2 中可以省略 `<template>` 标签直接书写插槽内容的行为也被严格限制,强制要求显式声明插槽边界[^5]。 --- ### 总结 Vue3 的 `<slot>` 设计更加强调一致性与灵活性,同时提供了更好的开发体验和支持现代 JavaScript 特性的能力。相比 Vue2,它的语法规则更为直观且易于维护。 ```javascript // Vue3 推荐的作用域插槽写法 const ChildComponent = { template: ` <div> <slot name="content" :data="internalData"></slot> </div> `, setup() { const internalData = { message: '来自子组件的数据' } return { internalData } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值