结论先行:
slot 插槽,是子组件提供给父组件使用的一个占位符,父组件可以在这个占位符中填充任何模板代码。主要作用就是更好的拓展和定制化组件,例如弹窗组件、表格组件等。分为默认插槽、具名插槽和作用域插槽。
其中前两个都是渲染在父级,本质上就是替换,父组件渲染完毕之后替换对应的 slot;区别在于具名插槽就是给插槽取了名字;
而作用域插槽在组件内部渲染。本质上会把父组件的内容渲染成函数,子组件调用函数,并且将数据传递给它。当需要将子组件的数据交给父组件展示,此时就可以使用作用域插槽。给数据一个新的作用域,因此叫做作用域插槽。
应用场景:自定义的表格组件,允许用户传入自定义的结构 + 数据(从插槽中回传的)
1、什么是 slot ?
① 官方文档
官方文档 Vue2:插槽 — Vue.js
官方文档 Vue3:插槽 Slots | Vue.js
② 概念
slot(插槽)是一种用于分发内容的机制,一种用于在组件中传递内容的机制。
也就是说在组件模板中利用 slot 进行占位,然后在使用组件时,传入的组件内容也就是 HTML 片段,会分发的对应的 slot 中。
它允许你在组件的模板中定义带有特殊标记的区域,然后在使用该组件时填充这些区域。
通过 <slot></slot>
标签来定义一个插槽。这个插槽可以有默认内容,也可以是没有任何内容的。当使用该组件时,可以将内容插入到插槽中。
插槽可以有默认值,直接在 slot 里设置。
简单理解就是子组件中留下个“坑”,父组件可以使用指定内容来补“坑”。以下是一个简单的示例,展示了如何在 Vue 组件中使用插槽:
<!-- 父组件 -->
<template>
<div>
<h1>父组件</h1>
<ChildComponent>
<p>这是插入到子组件中的内容</p>
</ChildComponent>
</div>
</template>
<!-- 子组件 -->
<template>
<div>
<h2>子组件</h2>
<!-- 这里是插槽 -->
<slot></slot>
</div>
</template>
在上面的示例中,父组件中的
<ChildComponent>
标签包含了一个<p>
标签,作为插入到子组件中的内容。子组件中的<slot></slot>
标签表示一个插槽,它将会被父组件中的内容所填充。
③ 内容定义方式
在 Vue2.6.0 之后,使用 v-slot
或者语法糖 #插槽名
定义;在这之前使用 slot="插槽名"
定义。
跟 v-on 和 v-bind 一样,v-slot 也有缩写,也就是把参数之前的所有内容 (v-slot:) 替换为字符 #;
下面是三种 默认插槽 的内容定义方式,并且最后一个默认插槽的内容会覆盖掉上一个插槽内容。
使用 template 包裹内容,同时使用 v-slot 绑定一个名称。
v-slot 一般跟 template 标签使用 (template 是 html5 新出的标签内容模板元素,不会渲染到页面上, 一般被 vue 解析为内部标签)
<child>
<template>
<span style="color: red">这里是默认插槽内容1,字体为红色</span>
</template>
<template v-slot:default>
<span style="color: yellow">这里是默认插槽内容2,字体为h黄色</span>
</template>
<template #default>
<span style="color: blue">这里是默认插槽内容3,字体为蓝色</span>
</template>
</child>
④ 独占默认插槽的缩写
v-slot:default="slotProps" 可以简写为 v-slot="slotProps"
如果我们的插槽只有默认插槽时,组件的标签可以被当做插槽的模板来使用。
也就是可以将 v-slot 直接用在组件上
2、插槽的作用 ?
通过使用插槽,我们可以实现更加灵活和可复用的组件,增强组件的可复用性。
当一个组件需要接收外部传入的内容时,可以使用插槽来实现这个功能。
同时,插槽还可以指定默认内容,当没有提供具体内容时将显示默认内容。
通过插槽可以让用户更好的对组件进行拓展和定制化,可以通过具名插槽指定渲染的位置。
常用的组件例如:弹窗组件、布局组件、表格组件……
3、插槽的分类
① 默认插槽
不指定插槽 name 属性。也就是插槽的内容出口(不写名字出口会带有隐含的名字“default”)
- 默认插槽是指在父组件中没有提供具名插槽名称的情况下使用的插槽。父组件中未匹配到具名插槽的内容会被放置在默认插槽中。
- 在 Vue 2 中,默认插槽是不带任何属性的
<slot>
元素。 - 在 Vue 3 中,默认插槽使用
v-slot
指令来定义。
<slot name="default"></slot> 或者 <slot></slot>
<!-- 父组件 -->
<template>
<div>
<h1>父组件</h1>
<ChildComponent>
<p>这是插入到子组件中的内容</p>
</ChildComponent>
</div>
</template>
<!-- 子组件 -->
<template>
<div>
<h2>子组件</h2>
<!-- 这里是插槽 -->
<slot></slot>
</div>
</template>
② 具名插槽
渲染在父级(父组件渲染好了,子组件根据 slot 对应的名字来替换就可以);
本质上就是替换,普通插槽就是在父组件渲染完毕之后,使用子组件替换掉父组件的占位符 slot;
定义: <slot name="xxx">
使用:
- <template #xxx></template>;
- <template v-slot:xxx></template>
- 具名插槽允许子组件将内容分发到父组件中指定名称的插槽上。
- 在 Vue 2 中,可以通过给
<slot>
元素添加name
属性来创建具名插槽,然后在父组件中使用<template v-slot:slotName>
或者<slot name="slotName">
来引用具名插槽。 - 在 Vue 3 中,具名插槽被称为命名插槽,可以使用新的
v-slot
指令来定义。
<!-- 子组件 -->
<div>
<!-- 具名插槽:指定了name属性, 内容出口 -->
<slot name="name-1"></slot>
</div>
<!-- 父组件 -->
<child>
<template v-slot:name-1="slotProps">
<span style="color: red">
{{ slotProps.user.firstName }}
这里是具名插槽内容1,字体为红色
</span>
</template>
<template #name-1="{ user }">
<span style="color: yellow">
{{ user.firstName }}
这里是具名插槽内容2,字体为黄色
</span>
</template>
</child>
下面是两种 具名插槽 的内容定义方式,并且最后一个插槽的内容会覆盖掉上一个插槽内容。
注意:默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确
③ 作用域插槽
作用域插槽:把组件内的值取出来自定义显示内容。
作用域插槽在组件内部渲染。本质上会把父组件的内容渲染成函数,子组件调用函数,并且将数据传递给它。函数的返回值就会替换掉这个占位符。
- 作用域插槽允许子组件将数据传递到父组件中的插槽内容中,使得父组件可以使用子组件中的数据进行渲染。
- 在 Vue2 中,作用域插槽通过
<slot>
元素的name
属性以及<template slot-scope="props">
来实现。- 在 Vue 3 中,作用域插槽的语法发生了变化,使用
v-slot
指令和新的#
符号来指定插槽的作用域。
组件内变量绑定在 slot 上,然后使用组件 v-slot:插槽名字="变量" ,变量上就会绑定 slot 传递的属性和值,插槽可以自定义显示内容;
运用场景:自定义的表格组件,允许用户传入:自定义的结构 + 数据(从插槽中回传的)
scope:是我指定的作用域,这个作用域是插槽的作用域。可以获取插槽上除 name 属性外的其他自定义属性,来获取到插槽上的数据
在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽
<!-- 子组件 -->
<div>
<slot name="name-1" slotMessage="信息1" slotMessage2="信息2"></slot>
</div>
<!-- 父组件 -->
<child>
<template #name-1="scope">
<!-- 下面这些值将会展示到插槽出口位置 -->
<span>{{ scope }}</span><br>
<span>{{ scope.slotMessage1 }}</span><br>
<span>{{ scope.slotMessage2 }}</span><br>
</template>
</child>
当需要将子组件的数据交给父组件展示,此时就可以使用作用域插槽。在子组件中,在 slot 中使用v-bind 绑定需要传递的数据
<slot :testProps="list">插槽后备内容</slot>
//在父组件中的子组件名中使用v-slot="属性名"的方式接受
<Child>
<template v-slot="testProps">
<p v-for="item in testProps" :key="item.id"> {{ item.name }} </p>
</template>
</Child>
4、动态插槽名
目前我们使用的插槽名称都是固定的,比如 v-slot:left、v-slot:center 等等;
我们可以通过 v-slot:[dynamicSlotName] 方式动态绑定一个名称;
4、Vue2 和 Vue3 的区别
① 都能使用 v-slot:name 或者其缩写 #name 的方式来定义具名插槽;
② 如果在父组件中传入多个插槽内容,那 Vue2 总是会展示最后一个插槽的内容,Vue3 会展示第一个插槽内容。
③ 作用域插槽,Vue2.x 的机制导致作用域插槽变了,父组件会重新渲染。而 Vue3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
5、总结
① v-slot
属性只能在 <template>
上使用,但在只有默认插槽时可以在组件标签上使用;
② 默认插槽名为 default
,可以省略 default 直接写v-slot;
③ 缩写为 #
时不能不写参数,写成 #default
④ 可以通过解构获取 v-slot={user}
,还可以重命名 v-slot="{user: newName}"
和定义默认值 v-slot="{user = '默认值'}"
⑤
在Vue中有渲染作用域的概念:
- 父级模板里的所有内容都是在父级作用域中编译的;
- 子模板里的所有内容都是在子作用域中编译的;
- 组件 ChildCpn 中,可以拿到自己作用域中的 title 内容;
- 但是在 App 中,是访问不了 ChildCpn 中的内容的,因为它们是跨作用域的访问;