什么是插槽?

组件插槽的概念就和生活中插槽的概念是一样的。比如主板上的内存插槽,只是给使用者留一个口子,具体插 8G 内存条还是 16G 内存条,取决于使用者的选择。
组件插槽也是一样的,我们设计好一个组件,并预留了插槽 slot,该组件被其他组件使用的时候,其他组件就能有选择的将什么内容插入该组件。
举个栗子:假如我们定制一个通用的导航组件 - NavBar。这个组件分成三块区域:左边-中间-右边,每块区域的内容是不固定;

  • 左边区域可能显示一个菜单图标,也可能显示一个返回按钮,可能什么都不显示;
  • 中间区域可能显示一个搜索框,也可能是一个列表,也可能是一个标题,等等;
  • 右边可能是一个文字,也可能是一个图标,也可能什么都不显示;

image.png
因此我们就能给 NavBar 设计左、中、右设计三个插槽。

如何使用插槽 slot?

设计插槽的过程实际就是找到相同的结构,但内容不同的地方。
在封装组件中,使用特殊的元素<slot>就可以为封装组件开启一个插槽;

  1. <template>
  2. <div>
  3. <son-component>
  4. <h3>我将被插入 son-component 组件的插槽内</h3>
  5. </son-component>
  6. </div>
  7. </template>
  8. <script>
  9. import sonComponent from './components/sonComponent.vue'
  10. export default {
  11. components: { sonComponent },
  12. }
  13. </script>
  14. ---------son-component----------
  15. <template>
  16. <div>
  17. <h1>下面是个插槽</h1>
  18. <slot></slot>
  19. <h1>上面是个插槽</h1>
  20. </div>
  21. </template>

插槽默认值

有时候我们希望在使用插槽时,如果没有插入对应的内容,那么我们需要显示一个默认的内容

  1. <template>
  2. <div>
  3. <h1>下面是个插槽</h1>
  4. <slot>
  5. <h2> 我是插槽中的默认内容</h2>
  6. </slot>
  7. <h1>上面是个插槽</h1>
  8. </div>
  9. </template>

具名插槽

主板上有很多插槽,便于用户正确使用都取了名字,内存插槽、显卡插槽、硬盘插槽。组件中的插槽也需要名字用于区分。 元素有一个特殊的 attribute:name,一个不带 name 的slot,会带有隐含的名字 default。
因此如果组件中有多个插槽,没有自己命名,默认都是同一个名字 default,而在使用的时候组件的时候,直接写在组件标签中的内容默认插的就是名为 default 的插槽。所以最后就会表现出来,内容在所有插槽中都插了一遍。

设计组件的时候,name attribute 给 slot 取名字。
使用的时候,用 template 标签包裹插入的内容,**v-slot:插槽名**指定插哪个插槽。
v-slot:语法糖为#

  1. <template>
  2. <div>
  3. <son-component>
  4. <template v-slot:hhh>
  5. <h3>我将被插入 son-component 组件中的 hhh 插槽内</h3>
  6. </template>
  7. </son-component>
  8. </div>
  9. </template>
  10. ---------son-component----------
  11. <template>
  12. <div>
  13. <h1>下面是个插槽</h1>
  14. <slot name="hhh">
  15. <h2> 我是插槽中的默认内容</h2>
  16. </slot>
  17. <h1>上面是个插槽</h1>
  18. </div>
  19. </template>

动态插槽名

我们可能会封装一些自动化程度高一点的组件。比如使用者通过一些配置文件,让组件自动生成,就像 spring 一堆的 xml 一样。组件自动生成,那组件中插槽名肯定也是使用者配置的。
这个也不难,使用者要指定组件的插槽名,其实就父向子组件传值。组件的插槽名定义在 name 属性上,设计的组件直接 v-bind 绑定 name 属性就行了。
其中注意的是,使用组件的时候,要用方括号计算,v-slot:[配置的插槽变量名]动态获取插槽名。

  1. <template>
  2. <div>
  3. <input type="text" placeholder="输入配置组件的插槽名" v-model="name">
  4. <!-- 父向子传值 -->
  5. <son-component :slotName="name">
  6. <!-- 方括号计算动态获取插槽名 -->
  7. <template #[name]>
  8. <h3>我将被插入 son-component 组件中的 {{ name }} 插槽内</h3>
  9. </template>
  10. </son-component>
  11. </div>
  12. </template>
  13. <script>
  14. import sonComponent from './components/sonComponent.vue'
  15. export default {
  16. components: { sonComponent },
  17. data() {
  18. return {
  19. name: null
  20. }
  21. },
  22. }
  23. </script>
  24. ---------son-component----------
  25. <template>
  26. <div>
  27. <h1>下面是个插槽</h1>
  28. <!-- 绑定插槽名 -->
  29. <slot :name="slotName">
  30. <h2> 我是插槽中的默认内容</h2>
  31. </slot>
  32. <h1>上面是个插槽</h1>
  33. </div>
  34. </template>
  35. <script>
  36. export default {
  37. props: ['slotName']
  38. }
  39. </script>

渲染作用域

在Vue中有渲染作用域的概念:

  • 父级模板里的所有内容都是在父级作用域中编译的;
  • 子模板里的所有内容都是在子作用域中编译的;

比如插入插槽的内容是写在父组件中,所以编译是在父组件中完成的,那么它无法访问到子组件中的 data 。

作用域插槽

但有时候我们想突破这个渲染作用域的限制,希望插槽可以访问到子组件中的内容。这个Vue给我们提供了作用域插槽。
子组件中插槽标签 可以添加自定义属性,从而携带数据送出去。
使用组件时,也要用 template 包裹,然后具名插槽基础上添加 = ,改为:**v-slot:插槽名="变量"**,这个变量是个对象,里面保存了自定义属性的键值对。

  1. <template>
  2. <div>
  3. <son-component>
  4. <!-- 获取具名插槽 zs 的自定义属性的值 -->
  5. <template v-slot:zs='slotProps'>
  6. <h3>插槽组件中的数据 {{ slotProps.msg }}</h3>
  7. </template>
  8. </son-component>
  9. </div>
  10. </template>
  11. <script>
  12. import sonComponent from './components/sonComponent.vue'
  13. export default {
  14. components: { sonComponent },
  15. }
  16. </script>
  17. ---------son-component----------
  18. <template>
  19. <div>
  20. <p>下面是个插槽</p>
  21. <slot :name="slotName" :msg="message">
  22. <h3> 我是插槽中的默认内容</h3>
  23. </slot>
  24. <p>上面是个插槽</p>
  25. </div>
  26. </template>
  27. <script>
  28. export default {
  29. data() {
  30. return {
  31. slotName: 'zs',
  32. message: 'hhh'
  33. }
  34. },
  35. }
  36. </script>

如果插槽名为默认的 default,则可以将v-slot:default='slotProps'写成v-slot="slotProps"
并且设计的插槽只有默认插槽时,组件的标签可以被当做插槽的模板 template 来使用,这样,我们就可以将 v-slot 直接用在组件标签上。

  1. <template>
  2. <div>
  3. <!-- 省去 template 并直接写到组件标签上 -->
  4. <son-component v-slot='slotProps'>
  5. <h3>插槽组件中的数据 {{ slotProps.msg }}</h3>
  6. </son-component>
  7. </div>
  8. </template>

如果存在默认插槽和具名插槽混合,那么就得按照完整的 template 来编写。