一、参考资料
二、运行环境
- Windows11
- Visual Studio Code v2022
- Node.js v16.5.01
- Vue/cli v5.0.6
- Bootstrap 5.1.3
三、Vue2插槽
Vue2 实现了一套内容分发的 API,,将 <slot>
元素作为承载分发内容的出口。
作用: 父组件通过定义插槽向子组件中传递定制化的内容(数据或函数)。
3.1 默认插槽
接下来举一个案例来体现插槽的作用。
比如在App组件中:
<template>
<div>
<Header></Header>
</div>
</template>
我定义了一个模板,里面有一个div,div里面引入了自定义的Vue组件Header
如果我想在App组件里引入Header组件,并向里面添加内容,比如:
<Header>
<h1> 搜索引擎 </h1>
</Header>
在默认情况下,<h1>
是不会渲染出来的,当然其他普通的HTML元素也不会渲染,此时就可以使用Vue提供的 slot 插槽,我们放一个插槽在这个Header组件里面,类似这样:
(如果引入Header组件时,里面没有内容,那么就会出现Header里<slot>
标签的内容:“插槽为空”
<template>
<div>
<slot>插槽为空</slot>
</div>
</template>
这仿佛就是在提示Vue:<slot>
这里是一个待填的坑,将由父组件来填。
由于是父组件来填的,所以 slot 插槽可用于父组件向子组件传递数据。
注意,通过 slot 插槽渲染的元素,其样式可以通过父组件来设置。
3.2 具名插槽
据Vue官方的文档介绍,所谓的具名插槽其实就是有name属性值的插槽,(从vue2.6.0开始,默认的插槽的name值为default),比如我们可以在同一个组件中设置多个插槽,这样这个组件的父组件就可以向不同的插槽里插入元素。
同样以App组件 和 Header组件为例。
Header 子组件:
<template>
<div>
<slot name="a">插槽a为空</slot>
<slot name="c">插槽c为空</slot>
</div>
</template>
App父组件:
<template>
<div>
<Header>
<h1 slot="a">插槽a</h1>
<h1 slot="c">插槽c 内容1</h1>
<h1 slot="c">插槽c 内容2</h1>
</Header>
</div>
</template>
上述App组件中的h1标签可以是其他的HTML标签。
这里指定的slot是可以复用的,不过插入相同插槽的元素可以用template标签来指定子组件的插槽,上述App组件代码的简写形式如下:
<template>
<div>
<Header>
<h1 slot="a">插槽a</h1>
<template slot="c">
<h1>插槽c 内容1</h1>
<h1>插槽c 内容2</h1>
</template>
</Header>
</div>
</template>
注意:上述在template直接使用slot的写法已经在Vue2.6.0 起被废弃 查看文档
Vue2.6.0 版本以后的 具名插槽 用法如下 查看文档:
声明插槽的方式不变(不过添加了default默认的插槽机制):
<!-- 插槽名为 slot1 -->
<slot name="slot1"></slot>
<!-- 插槽名默认为default -->
<slot></slot>
父组件在引入指定插槽时候,template指定插槽时必须使用 v-slot
,类似于 v-on 的@
和 v-bind的:
,v-slot
其简写形式为 #
,比如:
<template>
<div>
<Header>
<h1 slot="a">插槽a</h1>
<template #c>
<h1>插槽c 内容1</h1>
<h1>插槽c 内容2</h1>
</template>
</Header>
</div>
</template>
注意: v-slot 属性只支持 <template>
标签
同时, 由于插槽默认名称为 "default ",所以父组件可以指定默认插槽的内容:
<Header>
<template v-slot:default>
<h1>插槽c 内容1</h1>
<h1>插槽c 内容2</h1>
</template>
</Header>
<!-- 简写形式如下 -->
<Header>
<template #defualt>
<h1>插槽c 内容1</h1>
<h1>插槽c 内容2</h1>
</template>
</Header>
3.3 作用域插槽
使用场景:父组件渲染元素时候会用到子组件的数据,此时可通过作用域插槽,将设置插槽的子组件里的数据传给父组件,方便父组件往子组件里设置元素。
在Vue2中引入子组件时,父组件可以通过:Key=Value来传值,比如在App组件中:
<Header :name="uni"/>
这里在引入Header组件的同时,传入了名为name,值为"uni"的变量。
然后在Header组件中就可以通过 {
{ name }}
直接获取这个值。
而 <slot>
标签也可以通过这种方式传入数据,比如在Header子组件中:
<template>
<div>
<slot :name="uni">插槽a为空</slot>
</div>
</template>
这个 name变量就会传入到引入这个Header组件的父组件 App,然后父组件App在写插槽元素时就可以获取到数据了:
<template>
<div>
<Header>
<template slot-scope="data">
<h1> {
{ data.name }}</h1>
</template>
</Header>
</div>
</template>
这里的 slot-scope 声明了被接收的 prop 对象会作为 slotProps 变量存在于 作用域中。我们可以像命名 JavaScript 函数参数一样随意命名 data。
说起来可能会比较绕,简单理解就是作用域插槽,可以让子组件传递数据给父组件,不过父组件只能在引入该组件标签的内部获取到数据。
绑定在 <slot>
元素上的 attribute 被称为 插槽 prop。
关于作用域插槽,官方提到的一句很重要的话:
-
父级模板里的所有内容都是在父级作用域中编译的;
-
子模板里的所有内容都是在子作用域中编译的。
注意:以上通过 slot-scope来指定作用域的方法自Vue2.6.0开始已被废弃 官方文档
接下来是Vue2最新版本的作用域插槽用法 官方文档
<template v-slot:default="data">
{
{ data.name }}
</template>
<!-- 简写形式如下: -->
<template #default="data">
{
{ data.name }}
</template>
其实这里和之前差不多,新版本的改动就是 template必须使用v-slot来指定插槽,或者指定插槽引入的数据。
和默认插槽一样,如果在引入数据时没有指定插槽名称,那么数据将从默认插槽中获取
<template v-slot="data">
{
{ data.name }}
</template>
这里需要注意 v-slot="data"
和 v-slot:default
二者的区别,前者是从默认插槽里获取数据,定义为data,后者是指定为默认插槽。
ES6解构赋值概念 & 作用域插槽的解构赋值
“ES6是门脚本语言,从名字(ECMAScript6)中我们可以看出,他是JS的组成部分,直白点说,它规定了我们怎样写JS。”
ES6解构赋值,是指我们在写JavaScript代码时可以实现的一些特殊机制。
在解构中,有下面两部分参与:
- 解构的源,解构赋值表达式的右边部分。
- 解构的目标,解构赋值表达式的左边部分。
在Vue2作用域插槽中的解构赋值,是将参数值转为的Object对象,所以我们先主要了解ES6关于对象的解构赋值的一些机制:
(这里注意,等式的左边相当于子组件在定义slot标签时传入的参数,等式的右边则相当于父组件在插入元素到指定slot时,在v-slot:xxx=“Object” 中的Object)
- 基本的解构
let {
foo, bar } = {
foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let {
baz : foo } = {
baz : 'ddd' };
// foo = 'ddd' , 可理解为替换了Key=baz的Value
- 可嵌套可忽略
let obj = {
p: ['hello', {
y: 'world'}] };
let {
p: [x, {
y }] } = obj;
// obj.x = 'hello'
// obj.y = 'world'
// PS: 之前我们使用的作用域插槽就类似于这种,父组件只接收数据赋值为 obj , 然后我们通过obj来调用子组件在<slot>里面指定的属性
let obj = {
p: ['hello', {
y: 'world'}] };
let {
p: [x, {
}] } = obj;
// x = 'hello'
- 不完全解构
let obj = {
p: [{
y: 'world'}] };
let {
p: [{
y }, x ] } = obj;
// x = undefined
// y = 'world'
// 尽管obj里的p对象中只有一个元素,但是它同样可以赋值给具有两个元素的对象,多出来的对象就为undefined类型
- 剩余运算符
let {
a, b, ...rest} = {
a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40} 剩余的元素将以对象的形式赋值给rest
- 解构默认值(只会根据相应的Key进行替换,而不会替换整个Object对象)
let {
a = 10, b = 5} = {
a: 3};
// a = 3; b = 5;
let {
a: aa = 10, b: bb = 5} = {
a: 3};
// aa = 3; bb = 5;
Array数组模型的解构和对象类似,不过多描述了,在Vue2中主要用到的是对象解构可以参考 点击查看
作用域插槽的内部工作原理是将插槽的内容包裹在一个拥有单个参数的函数里。
解构插槽 Prop ,这是官方在介绍作用域插槽时的拓展内容,我觉得有必要Mark一下,将来使用组件的时候可能会用到。
根据官方解释,我们举个实际的例子来理解一下这句话:
- 子组件Header组件定义一个插槽
<template>
<d