Vue slot插槽作用与原理深入讲解
AI3D_WebEngineer 人气:0前言
在2.6.0中,具名插槽 和 作用域插槽 引入了一个新的统一的语法 (即v-slot 指令)。它取代了 slot 和 slot-scope。
什么是Slot
当我们生成好组件模板后,为了方便用户在调用组件的时候自定义组件内部分元素的样式或内容。于是在设计组件模板的时候会挖一个坑,等待用户使用v-slot来替换坑的slot位置。
栗子
// father.vue <template> <div> <child> <h1>AAAA</h1> </child> </div> </template>
<template> <div> <p>这里是子组件哦</p> <slot></slot> </div> </template>
可以看见 <h1>AAAA</h1>被 插入到child的里面。
除了可以插入HTML模板代码外,你也可以插入普通文本、其他组件、本组件的数据。
在插槽中使用数据
// father.vue <template> <div> <child> {{testName}} <h1>AAAA</h1> </child> </div> </template> <script> ... testName = 'Test101'; ... </script>
插槽可以使用当前引用子组件的组件数据,但是不能引用传递给子组件的数据。
// father.vue <template> <div> <child :childName="testName"> {{ childName }} <h1>AAAA</h1> </child> </div> </template> <script> ... testName = 'Test101'; ... </script>
// child.vue <template> <div> <p>这里是子组件{{ childName }}</p> <slot></slot> </div> </template> <script> import { Component, Vue, Prop } from 'vue-property-decorator'; @Component() export default class AAChild extends Vue { @Prop() childName; } </script>
这里是获取不到childName的,因为这个值是传给<child>的
备胎插槽
// father.vue <div> <child :childName="testName"> </child> </div>
// child.vue <div> <p>这里是子组件{{ childName }}</p> <slot> 我是备胎 </slot> </div>
给插槽设置一个具体的默认内容,当别的组件没有给你内容的时候,那么默认的内容就会被渲染。如果我们提供内容,那么默认的插槽内容会被我们的内容覆盖。
具名插槽
当我们一个组件里需要定义多个插槽时,需要使用slot元素的特性name来定义额外的插槽。
// child.vue <template> <div> <p>插槽一的位置</p> <slot name="slot1"> </slot> <p>------------------------</p> <p>插槽二的位置</p> <slot> </slot> <p>------------------------</p> <p>插槽三的位置</p> <slot name="slot3"> </slot> </div> </template>
// father.vue <child> <template v-slot:slot1> <h1>slotOne</h1> </template> <template> <h1>slotTwo</h1> </template> <template v-slot:slot3> <h1>slotThree</h1> </template> </child>
如果一个<slot>不带name属性的话,那么它的name默认为default,可以不用v-slot去指定它。(如果你非要折腾,也可以写name="default")
在向具名插槽提供内容的时候,我们可以在<template>元素上使用v-slot指令,并以参数的形式提供其名称。
注:v-slot只能添加在一个<template>上,(只有一种例外情况,下面会说)
覆盖问题
当存在同名的v-slot的时候,后面会覆盖前面的。
当存在两个匿名的插槽的时候,两者都会被丢进去默认插槽里。
只更改了father.vue
// father.vue <child> <template v-slot:slot1> <h1>slotOne</h1> </template> <template> <h1>slotTwo</h1> </template> <template v-slot:slot3> <h1>slotThree</h1> </template> <template v-slot:slot3> <h1>slotThreeAAAAAAAAA</h1> </template> <template> <h1>slotTwoAAAAAAAAAAAA</h1> </template> </child>
作用域插槽
插槽跟模板其他地方一样都可以访问当前father组件的作用域而不能访问<child>的作用域
如果要访问child的作用域该怎么办呢?
// child.vue <template> <div> <p>下面有一个插槽</p> <slot :aName="name"></slot> </div> </template> <script> import { Component, Vue, Prop } from 'vue-property-decorator'; @Component() export default class AAChild extends Vue { name = '123'; } </script>
// father.vue <template> <div> <child> <template v-slot:default="slotProps"> <h1>{{ slotProps.aName }}</h1> </template> </child> </div> </template>
v-slot:default="slotProps"接住了child组件里在slot元素上绑定的作用域属性,同时吧template只想了默认插槽。
具名插槽的作用域
// father.vue <template> <div> <child> <template v-slot:default="slotProps"> <h1>{{ slotProps.aName }}</h1> </template> <template v-slot:slotA="slotProps"> <h1>{{ slotProps.nameA }}</h1> </template> </child> </div> </template>
// child.vue <template> <div> <p>下面有一个插槽</p> <slot :aName="name"></slot> <slot :nameA="nameA" name="slotA"></slot> </div> </template> <script> import { Component, Vue, Prop } from 'vue-property-decorator'; @Component() export default class AAChild extends Vue { name = '123'; nameA = '321'; } </script>
过时的写法:
<template slot="custom" slot-scope="item"></template >
现在的写法:
<template v-slot:custom="{item}"></template >
解构插槽Prop
v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JS 表达式(作用域插槽 的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里)
// child.vue <template> <div> <p>下面有一个插槽</p> <slot :nameData="nameObj"></slot> </div> </template> <script> import { Component, Vue, Prop } from 'vue-property-decorator'; @Component() export default class AAChild extends Vue { nameObj = { name: '插槽君', key: '202203241567', }; } </script>
// father.vue <template> <div> <child> <template v-slot="slotProps"> <h1>{{ slotProps.nameData.name }}</h1> </template> </child> </div> </template>
father,vue可以改写为
// father.vue <template> <div> <child> <template v-slot="{ nameData }"> <h1>{{ nameData.name }}</h1> </template> </child> </div> </template>
这样可以使模板更简洁。
具名插槽的缩写
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header:
// father.vue <template> <div> <child> <template v-slot:keyDDD="{ nameData }"> <h1>{{ nameData.name }}</h1> </template> </child> </div> </template>
等同于
// father.vue <template> <div> <child> <template #keyDDD="{ nameData }"> <h1>{{ nameData.name }}</h1> </template> </child> </div> </template>
$scopedSlots
和slot-scope 的区别?
- 作用相同:都是作用域插槽
- 场景不同:slot-scope 是模板语法,scopedSlots 则是编程式语法
- 使用不同:在 <template> 中使用 slot-scope,在 render() 函数中使用 scopedSlots
<template v-if="$scopedSlots.label" v-slot:title="{ data }"> <slot name="label" :data="data" > </slot> </template>
或者
render() { return this.$scopedSlots.default ? this.$scopedSlots.default(this.title) : null; }
加载全部内容