5月底看vue2的时候不太明白插槽,最近好像有点懂了,赶紧缕一缕
v-slot 指令(统一 slot 和 scope-slot 语法)
v-slot
指令自 Vue 2.6.0 起被引入,提供更好的支持slot
和slot-scope
的 API 替代方案。slot 和 slot-scope被废除,改为用v-slot
代替v-slot
指令是被废除的slot
和scope-slot
语法的统一,可以通过搞懂slot
和scope-slot
来理解v-slot
- 使用总结: (可看下方“完整例子”)
- 子组件中定义(具名)插槽,可绑定想要传给父组件使用插槽位置的变量
- 父组件中使用(具名)插槽,可获取并使用子组件中定义插槽时绑定的变量
v-slot简写#
- 跟
v-on
和v-bind
一样,v-slot
也有缩写,即**把v-slot:
替换为字符#
**。 - 例如:
v-slot:header
可以被重写为#header
,和其它指令一样,该缩写只在其有参数的时候才可用 (具体可看下方“完整例子”) - 注意:使用缩写时,default不可省略,比如:
插槽props
- 具体可见下方“插槽 prop(常命名为slotProps)父组件传值给插槽内容”
- 注意:当子组件中给插槽绑定对象/数组,父组件中修改对象/数组(非整个替换)时,会影响子组件中状态
- 下面例子中,改变a/b或者整个替换掉ab、arr
@click="() => slotProps.arr = [3]"
都不会影响子组件中变量
1 | <!-- 子组件,给插槽绑定ab对象和arr数组 --> |
完整例子
1 | <!-- 子组件child中假设有数据ab = { a:1, b:2 } --> |
区分 匿名插槽 和 插槽prop
- 匿名插槽使用方法:
- 也可使用默认插槽的缩写语法,直接写在调用子组件内**
<child>插槽内容</child>
**1
2
3<child>
<template v-slot:default>插槽内容</template>
</child>
- 也可使用默认插槽的缩写语法,直接写在调用子组件内**
- 插槽prop使用方法(例子参考}:
1
2
3
4
5
6
7
8<!-- 假设child内有数据ab={a:1,b:2}且<slot v-bind="a"> -->
<child>
<template v-slot:default="slotProps">插槽内容+slotProps.ab.a</template>
<!-- 或者解构插槽prop -->
<template v-slot:default="{ab}">插槽内容+ab.a</template>
<!-- 可使用默认插槽的缩写语法 -->
<template v-slot="{ab}">插槽内容+ab.a</template>
</child> - 当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这时我们就可以把 v-slot 直接用在子组件上,注意!这种情况下 默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确
- 例子里在Child标签上获取了一个slotProps,结果其中一个具名template又获取一次otherSlotProps,就乱了:
- 所以,只要出现多个插槽,请始终为所有的插槽使用完整的基于
<template>
的语法:
slot匿名/具名插槽
- 自 2.6.0 起,slot被废弃
- slot:组件的一块HTML模板
- 子组件:规定显示的位置 (
<slot></slot>
)1
2
3
4
5
6
7<!-- 假设以下是子组件navigation-link的内容,slot就是插槽位置 -->
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a> - 父组件:规定显示的HTML模板(内容)、显不显示 (
Your Profile
就是显示内容)1
2
3
4<!-- 假设navigation-link是子组件名,Your Profile就是子组件中插槽位置显示的内容 -->
<navigation-link url="/profile">
Your Profile
</navigation-link> - 上面的例子:当组件渲染的时候,**
<slot></slot>
将会被替换为Your Profile
**。
slot的name属性
- 多个slot时,可以通过name来加以区分。
- 匿名插槽:一个不带 name 的
<slot>
会带有**隐含的名字default
**。 - 使用方法:在父组件中使用子组件中加入template组件里面用**
v-slot
指令定义name**,对应子组件中<slot>
的name属性- 任何没有被包裹在具名插槽中(即带有
v-slot
的<template>
中)的内容都会被视为**默认插槽(default
,即未命名的插槽)**的内容。
- 任何没有被包裹在具名插槽中(即带有
例子
- 假设子组件为
<base-layout>
:1
2
3
4
5
6
7
8
9
10
11
12
13
14<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
<slot name="header"></slot>
</header>
<main>
<!-- 我们希望把主要内容放这里,这是默认插槽,相当于name="default" -->
<slot></slot>
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
<slot name="footer"></slot>
</footer>
</div> - 父组件中,使用子组件时:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<base-layout>
<!-- 以前的slot="header"在2.6.0以后被废除了 -->
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<!-- 以下内容出现在 main 里,这是默认插槽,相当于name="default" -->
<!-- <template v-slot:default>可有可无 -->
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout> - 结果:
1
2
3
4
5
6
7
8
9
10
11
12<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
v-slot可不在template上的唯一情况
- 注意:
v-slot
只能添加在<template>
上 (只有一种例外情况),这一点和已经废弃的 slot 不同。- 例外情况:当只存在默认插槽(不存在其他具名插槽)时,子组件的标签才可以被当作插槽的模板来使用。才可以把
v-slot
直接用在子组件的标签上- 注意:默认插槽的缩写语法 不能 和具名插槽混用,因为它会导致作用域不明确
- 例外情况:当只存在默认插槽(不存在其他具名插槽)时,子组件的标签才可以被当作插槽的模板来使用。才可以把
插槽内容作用域
- 插槽具体显示的内容是在父组件中规定的,所以插槽内容的作用域和父组件作用域一致
- 例子(navigation-link是子组件):
- 官方规则:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
- 所以处于父级模板里的 子组件插槽内容 是在父级作用域中编译的
- 但通过 插槽prop ,父组件可以在调用子组件中的数据
slot-scope作用域插槽(带数据的具名插槽)
- 自 2.6.0 起有所更新,slot和slot-scope已废弃,统一使用v-slot代替!
- 具体例子参考
- 与slot异同:
- 与slot相同,插槽位置 在子组件中通过
<slot>
规定。 - 与slot不同:
- 区别于 具名插槽的
v-slot:header
,作用域插槽使用 slot属性slot="header"
- slot插槽内容 是在使用子组件时的带了v-slot的template组件里面写;slot-scope是在带了slot属性(默认default可省略)的template/非template里面写插槽内容。
- 区别于 具名插槽的
v-slot:default="slotProps"
,作用域插槽的 slot-scope属性slot-scope="childData"
可直接获取 插槽propchildData
- 具名插槽只有在只存在默认插槽(不存在其他具名插槽)时,才可以把
v-slot
直接用在 非template组件 的标签上,不过作用域插槽的slot可以出现在 非template组件 上作为属性。
- 区别于 具名插槽的
- 与slot相同,插槽位置 在子组件中通过
- 在子组件内的作用域插槽上使用
slot-scope
属性,可以接收子组件内插槽绑定的传递给父组件的数据(即插槽prop): - slot-scope属性也可以**直接用于非
<template>
元素 (包括组件上)**:
例子
- 子组件中:
1
2
3
4
5
6
7<div class="child">
<div>
<!-- 插槽命名为default,绑定数据msg给父组件的插槽prop -->
<slot name="default" :msg="msg"> </slot>
<p>这里是child 组件</p>
</div>
</div> - 父组件中:
插槽内容作用域
- 和slot的异同:
- 同样的,插槽的内容是子组件传递给父组件的,不是在父组件中定义的
- 不同的是,插槽内容和父组件享有同样的作用域,但无法获取子组件内部的数据,但插槽内容可通过插槽 prop获取子组件内的数据。
插槽 prop(常命名为slotProps)子组件传值给父组件使用插槽位置
- 插槽 prop
- 插槽内容和父组件享有同样的作用域,但无法获取子组件内部的数据,但父组件中的插槽内容可通过插槽 prop获取子组件内的数据
- 注意:slotProps可自定义命名,是子组件中所有 插槽prop 的对象的集合对象
- 插槽prop:子组件插槽中绑定的属性
- 例子:
- 注意: 如果使用
v-bind="{a: 1, b: 2}"
直接绑定插槽属性,则插槽调用是获取到的 插槽prop 直接就是{a: 1, b: 2}
,而不是{{a: 1, b: 2}}
1
2
3
4
5
6
7
8<!-- 子组件中,给插槽绑定user属性 -->
<slot v-bind="{a: 1, b: 2}">
</slot>
<!-- 父组件中,可获取所有插槽绑定属性(即子组件内数据) -->
<child v-slot="{a, b}">
{{ a }} // 1
</child>
父组件插槽中修改slotProps是否影响子组件中状态
- 当子组件中给插槽绑定对象/数组,父组件中修改对象/数组(非整个替换)时,会影响子组件中状态
- 下面例子中,改变a/b或者整个替换掉ab、arr
@click="() => slotProps.arr = [3]"
都不会影响子组件中变量
1 | <!-- 子组件,给插槽绑定ab对象和arr数组 --> |
独占默认插槽的缩写语法不能和具名插槽混用
- 参考文档
- 当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这时我们就可以把 v-slot 直接用在子组件上,但此时 默认插槽的缩写语法 不能和 具名插槽 混用,因为它会导致作用域不明确
- 例子里在Child标签上获取了一个slotProps,结果其中一个具名template又获取一次otherSlotProps,就乱了:
- 只要出现多个插槽,请始终为所有的插槽使用完整的基于
<template>
的语法: