1. 前言
回顾VUE 2
插槽,插槽在日常开发过程中组件间的交互应用之广,此笔记以官方文档的插槽介绍为顺序进行回顾
以官方docs
为主 —— 链接地址
- 笔记演示内容文件结构
view
slot
custom
index.vue // <slot> </slot> 插槽的 出口 —————— 子组件
manage
index.vue // 使用 custom 组件中插槽的 入口 —————— 父级组件
2. 插槽内容(默认插槽)与 后备内容
custom>index.vue
子组件 保持不变
<template>
<div class="custom">
<!-- 定义一个插槽(挖一个坑,等着组件的使用者进行填充) -->
<slot> 调用组件者,没有让内容进入插槽入口 </slot>
</div>
</template>
- 使用
custom
组件者(父级组件)向插槽内传递具体结构时
manage>index.vue
<template>
<custom>进入了custom组件插槽入口</custom>
</template>
<script>
import custom from '../custom';
export default {
components: {
custom,
},
};
</script>
manage>index.vue
显示效果如下:(以HTML代码为内容)
<div class="custom">进入了custom组件插槽入口</div>
- 使用
custom
组件者(父级组件)并未向插槽内传递内容时
<template>
<custom />
</template>
<script>
import custom from '../custom';
export default {
components: {
custom,
},
};
</script>
**注意:**若custom>index.vue
没有<slot>
标签的插槽出口或有<slot>
标签但不向其传递内容时,在调用它时,<custom />
和 <custom></custom>
的写法都可以,但若要向调用子组件的插槽传递内容时,写法必须是<custom></custom>
manage>index.vue
显示效果如下:(以HTML代码为内容)
<div class="custom"> 调用组件者,没有让内容进入插槽入口 </div>
可以发现,若使用custom
组件者(父级组件)并未向插槽内传递内容时,子组件custom
就会使用它的后备内容,即子组件<slot></slot>
标签包裹的内容
3. 插槽编译作用域
- 父组件
custom>index.vue
<template>
<custom :no="1">
{{ msg }}<br />
号码:{{ no }}
</custom>
</template>
<script>
import custom from '../custom';
export default {
components: {
custom,
},
data() {
return {
msg: '父组件进入子组件',
};
},
};
</script>
- 子组件
custom>index.vue
<template>
<div class="custom">
<!-- 定义一个插槽(挖一个坑,等着组件的使用者进行填充) -->
<slot> 调用组件者,没有让内容进入插槽入口 </slot>
</div>
</template>
<script>
export default {
data() {
return {
no: '2',
};
},
};
</script>
该插槽跟模板template
的其它地方一样可以访问相同的实例 property (也就是相同的“作用域”),而不能访问 <custom>
的作用域。
这里的 no
会是 undefined,因为其<custom :no="1">
上的no
内容是传递给<custom>
子组件 的而不在 custom
子组件内部定义的。
4. 具名插槽
具名插槽,根据名称我们大致可以推测出其插槽限定了名字
在子组件的<slot>
元素有一个特殊的属性name
,以name
来限定父组件中传递相同name
的结构内容到子组件插槽中
- 父组件
manage>index.uve
<template>
<custom>
<template slot="header"> 头部</template>
没有具名,就传递给默认插槽咯
<!-- <template slot="default"> 没有具名,就传递给默认插槽咯</template> 默认插槽上下两种写法-->
<template slot="footer">尾部 </template>
</custom>
</template>
- 子组件
custom>index.vue
<template>
<div class="custom">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
manage>index.vue
显示效果如下:(以HTML代码为内容)
<div class="custom">
<header> 头部 </header>
<main> 没有具名,就传递给默认插槽咯 </main>
<footer> 尾部 </footer>
</div>
注意:我们对于header
|main
|footer
,不可能重复写三个组件标签<custom slot="xxx">
,这样会导致代码冗余度过大,影响解析效率,而Vue
也考虑到了这点
只需在template
标签中或者其它标签(如h1
|p
等)使用slot
属性,并赋予想要父组件内容传递到子组件中的插槽名为值,即可将内容插入对应的插槽
当然,任何没有被包裹在带有 slot
的 <template>
或其他标签 中的内容都会被视为默认插槽的内容。
子组件默认插槽的 <slot>
出口会带有隐含的名字“default” = <slot name="default"></slot>
,只不过name=default
是可以隐藏的
- 需要注意的是,
vue 2.6.0
版本后废弃slot
,改成v-slot
,v-slot
只能添加在<template>
上 (只有一种例外情况),这一点和已经废弃的slot
attribute 不同。使用v-slot
重新编写父组件manage>index.vue
如下:
<template>
<custom>
<template v-slot:header> 头部 </template>
<template v-slot:default> 没有具名,就传递给默认插槽咯</template>
<template v-slot:footer> 尾部 </template>
</custom>
</template>
<script>
import custom from '../custom';
export default {
components: {
custom,
},
};
</script>
- 需要注意的是:
v-slot
也可以缩写,例如:在父组件中v-slot:header
可以简写为#header
,如下:
<template>
<custom>
<template #header> 头部 </template>
<template #default> 没有具名,就传递给默认插槽咯</template>
<template #footer> 尾部 </template>
</custom>
</template>
<script>
import custom from '../custom';
export default {
components: {
custom,
},
};
</script>
5. 作用域插槽
vue 2.6.0
版本前使用属性slot-scope
,而2.6.0
以后废弃这一属性,采用v-slot
来替代,废弃的slot-scope
可参考=>链接跳转
- 子组件中访问父组件传递的内容
这里与插槽无关,父组件向子组件传递数据,我们通常在父组件上设定好要传入的参数名和父组件的变量值,子组件在props
中便可获取到,不仅是变量,函数等也可以的。
- 有时让插槽内容能够访问子组件中才有的数据是很有用的,以下演示父组件
mange>index.vue
访问子组件custom>index.vue
的数据
mange>index.vue 父组件
<template>
<custom>
<template #header="{ user }"> 头部用户名:{{ user.name }} </template>
<template #default> 没有具名,就传递给默认插槽咯</template>
<template #footer> 尾部 </template>
</custom>
</template>
<script>
import custom from '../custom';
export default {
components: {
custom,
},
};
</script>
custom>index.vue 子组件
<template>
<div class="custom">
<div>客户信息:姓名({{ user.name }}),年龄({{ user.age }})</div>
<header>
<!-- 为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot> 元素的一个 属性 绑定上去 -->
<slot name="header" :user="user"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: '123', age: 18 },
};
},
};
</script>
manage>index.vue
显示效果如下:(以HTML代码为内容)
<div class="custom">
<div>客户信息:姓名(123),年龄(18)</div>
<header> 头部用户名:123 </header>
<main> 没有具名,就传递给默认插槽咯</main>
<footer> 尾部 </footer>
</div>
绑定在子组件 <slot name="header" :user="user">
元素上的 属性user
被称为插槽 prop。
父组件通过v-slot
来接收,这里因为我们在具名插槽上绑定元素,所以在父组件接受时要写为v-slot:header
,而v-slot:
可以简写为#
,但要注意的是,当我们在父组件中将v-slot
作用在子组件名字身上,而非template
上时,v-slot
缩写时,要写成如:<custom #default={user}>
,<custom #="{user}">
会报一个警告。
v-slot:header
后面的='xxx'
,即为接受子组件所传的prop
对象,这里额xxx
我们可以用任意名称代替。
由于作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里,这意味着
v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。
所以我们可以通过解构来传入具体的插槽prop
(第二种),或者将 user
重命名为 person
(第三种),甚至可以定义后备内容(第四种),用于插槽 prop 是 undefined 的情形(。
<custom>
<!-- 第一种 (常规) -->
<template #header="slotProps">头部用户名:{{ slotProps.user.name }} </template>
<!-- 第二种 (解构) -->
<template #header="{ user }"> 头部用户名:{{ user.name }} </template>
<!-- 第三种 (重命名) -->
<template #header="{ user: person }"> 头部用户名:{{ person.name }} </template>
<!-- 第四种 (后备内容) -->
<template #header="{ user = { name: '张三' } }"> 头部用户名:{{ user.name }} </template>
</custom>
- 当父组件
manage>index.vue
只有默认插槽时,v-slot
可以直接作用在子组件上,同时要注意<custom></custom>
中不能出现<template v-slot:default>
等,否则会报错。
<custom v-slot="slotProps">
用户名:{{ slotProps.user.name }}
</custom>
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确
只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template>
的语法,如下
<custom>
<template #header="{ user }"> 头部用户名:{{ user.name }} </template>
<template #default="slotProps">
{{ `年龄:${slotProps.user.age}` }}
</template>
</custom>
6. 动态插槽名
vue 2.6.0
后新增,要注意自己的vue
版本号
- 动态指令参数也可以用在
v-slot
上,来定义动态的插槽名
父组件 manage>index.vue
<template>
<div>
<el-radio-group v-model="curSlotName">
<el-radio label="header">名字去头部</el-radio>
<el-radio label="default">名字去内容区</el-radio>
<el-radio label="footer">名字去尾部</el-radio>
</el-radio-group>
<custom>
<template v-slot:[curSlotName]> {{ '姓名:张三' }}</template>
</custom>
</div>
</template>
<script>
import custom from '../custom';
export default {
components: {
custom,
},
data() {
return {
curSlotName: 'default',
};
},
};
</script>
子组件 custom>index.vue
<template>
<div class="custom">
<header>
<slot name="header">头部</slot>
</header>
<main>
<slot>内容区</slot>
</main>
<footer>
<slot name="footer">尾部</slot>
</footer>
</div>
</template>
<script>
manage>index.vue
显示效果如下:
7. 结尾
以上是学习Vue 2插槽的全部内容,内容中可能存在不足或有误之处,还请指出修改。