携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
一、插槽的应用场景
props:单向数据流,常用于父组件给子组件传递值,属于数据层面
slot:我们引入了一个组件,但是想要差异化配置组件,并且向组件中添加自定义的内容。属于dom层面。
二、插槽的初步使用
【注】: 这里的插槽语法都是2.6.0以后的使用方法,而后面的源码使用的是比较低的版本的写法。
为表示区分,声明为child组件的样式都为蓝绿色
场景一:主页面A需要引入一个组件child
//A
<template>
<div>
<h3>主页面</h3>
<child> </child>
</div>
</template>
<script>
import child from "./child";
export default {
components: {
child,
},
};
</script>
<style>
</style>
//组件child
<template>
<div>
<div class="font">子组件</div>
</div>
</template>
<script>
export default {};
</script>
<style>
.font {
color: #05bbc9;
font-weight: 900;
}
</style>
达到的效果是主页面A可以使用组件B
场景二:主页面A需要在子组件中写入主页面的内容
//A
<template>
<div>
<h3>主页面</h3>
主页面自己的数据
<hr />
<child> 主页面写入组件的数据 </child>
</div>
</template>
<script>
import child from "./child";
export default {
components: {
child,
},
};
</script>
<style>
</style>
//组件child
<template>
<div>
<h4 class="font">子组件</h4>
<div class="font">子组件自己的数据</div>
<slot class="font"></slot>
</div>
</template>
<script>
export default {};
</script>
<style>
.font {
color: #05bbc9;
font-weight: 900;
}
</style>
理解
- 主页面在引用组件时传入了一段文本
- 组件声明了一个
<slot></slot>插槽接收主页面传递的内容
注:你可能会注意到,这里主页面写入组件的数据虽然引用了组件页面的样式,但是没有起作用。如果想要加字体样式,需要在主页面中声明child组件的样式为class="font"。
三、插槽的分类
1.后备内容(默认插槽)
场景:组件具有默认值,如果在使用时,主页面不传入数据,则使用默认值;否则使用主页面传入的值
方法:在声明插槽的标签中写入默认值即可
<slot class="font">如果主页面不传入数据,显示我</slot>
2.具名插槽
场景:组件中有多个插槽,主页面传入的数据需要渲染在不同的插槽里面
vue官方文档说明如下:
一个不带
name的<slot>出口会带有隐含的名字“default”。在向具名插槽提供内容的时候,我们可以在一个
<template>元素上使用v-slot指令,并以v-slot的参数的形式提供其名称:
方法:
1.在组件中的插槽域中声明name属性
2.在主页面引用时,使用<template v-slot:name></template>
<template>
<div>
<h3>主页面</h3>
主页面自己的数据
<hr />
<child>
<template v-slot:third>
<h3 class="font">third</h3>
</template>
<template v-slot:second>
<h3 class="font">second</h3>
</template>
<template v-slot:first>
<h3 class="font">first</h3>
</template>
<template>
<h3 class="font">default</h3>
</template>
</child>
</div>
</template>
<template>
<div>
<h4 class="font">组件</h4>
<div class="font">组件自己的数据</div>
<hr />
<div class="font">主页面传来的数据</div>
<div style="color: red">
<p>first 的数据</p>
<slot name="first"></slot>
</div>
<div style="color: red">
<p>second 的数据</p>
<slot name="second"></slot>
</div>
<div style="color: red">
<p>third 的数据</p>
<slot name="third"></slot>
</div>
<div style="color: red">
<p>default 的数据</p>
<slot></slot>
</div>
</div>
</template>
细节
- 主页面未指定v-slot属性的插槽自动渲染到没有name属性的slot标签中
- 主页面声明的顺序不影响组件中slot中元素排列的顺序
- v-slot可以简写为“#”,如同v-bind可以简写为“:”
3.作用域插槽
场景:解决主页面访问子组件的数据问题
子组件定义一个user对象,主页面希望访问此数据:
<template>
<div>
<h4 class="font">组件</h4>
<div class="font">组件自己的数据</div>
<hr />
<div class="font">主页面传来的数据</div>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: "zs",
password: "root",
},
};
},
};
</script>
运行报错:
这是因为子组件中的数据是在子组件中编译的,而主页面中的数据是在主页面中编译的。
解决方法:
在子组件的slot标签上绑定user对象
<slot :user="user"></slot>
主页面中使用该对象
<child>
<template v-slot:default="slotProps">
{{ slotProps.user.name }}
{{ slotProps.user.password }}
</template>
</child>
如果页面中只存在单个插槽,可以简写为:
<child>
<template v-slot="slotProps">
{{ slotProps.user.name }}
{{ slotProps.user.password }}
</template>
</child>
多个插槽的情况:
<template>
<div>
<h3>主页面</h3>
主页面自己的数据
<hr />
<child>
<template v-slot="slotProps">
{{ slotProps.job.rank }}
{{ slotProps.job.name }}
</template>
<template v-slot:first="slotProps">
{{ slotProps.user.name }}
{{ slotProps.user.password }}
</template>
<template v-slot:second="slotProps">
{{ slotProps.address.provice }}
{{ slotProps.address.city }}
</template>
</child>
</div>
</template>
<template>
<div>
<h4 class="font">组件</h4>
<div class="font">组件自己的数据</div>
<hr />
<div class="font">主页面传来的数据</div>
<div style="color: red">
<p>first 的数据</p>
<slot name="first" :user="user"></slot>
</div>
<div style="color: red">
<p>second 的数据</p>
<slot name="second" :address="address"></slot>
</div>
<div style="color: red">
<p>default 的数据</p>
<slot :job="job"></slot>
</div>
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: "zs",
password: "root",
},
address: {
provice: "sx",
city: "xa",
},
job: {
rank: "1",
name: "java",
},
};
},
};
</script>
vue官方文档提到上述报错的原因:
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为
slotProps,但你也可以使用任意你喜欢的名字。
此外,为了使用方便,我们可以解构slotProps对象:
<template>
<div>
<h3>主页面</h3>
主页面自己的数据
<hr />
<child>
<template v-slot="{ job }">
{{ job.rank }}
{{ job.name }}
</template>
<template v-slot:first="{ user }">
{{ user.name }}
{{ user.password }}
</template>
<template v-slot:second="{address}">
{{ address.provice }}
{{ address.city }}
</template>
</child>
</div>
</template>
当然,也可以进一步缩写,把v-slot换成“#”,在此不做赘述。
四、element-ui 中对slot插槽的使用
引入两个知识:
1.slot历史版本的问题
<template v-slot:first="{ user }">
{{ user.name }}
{{ user.password }}
</template>
与
<template slot="first" slot-scope="slotProps">
{{ slotProps.user.name }}
{{ slotProps.user.password }}
</template>
是等价的
2、vm.$slots:用来访问被插槽分发的内容。常用于JSX渲染函数
<template v-slot:testSlot> mainSlot </template>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<div style="color: red">
<p @click="printScope($slots.testSlot)">$slot 的数据</p>
<slot name="testSlot"> </slot>
</div>
3.阅读element-ui文档:
element-ui官方文档示例如下:
<div>
<el-input placeholder="请输入内容" v-model="input1">
<template slot="prepend">Http://</template>
/* 等价于*/
/* <template v-slot="prepend">Http://</template> */
</el-input>
</div>
如果我们配置了上述代码,那么上面template这行代码会被分发到源码中的
<slot name="prefix"></slot>位置,从而生成input的前缀。
(源码位置:element-ui/packages/input/src/input.vue)
五、总结
我们在使用组件库的时候,经常会接触到slot插槽的相关属性,每次都让人感觉到雾里看花。探究其本源以及查看源码中的使用方法似乎成了解密的唯一途径。
props和slot都是为了封装组件而生的,但是他们存在一些不同之处:
相同点:
- 都是发生在父子组件之间的关系;
- 都是为了应对父组件调用子组件的场合;
不同点
区别一:设计思想
props的设计思想是传递状态,将数据驱动组件的思想贯彻到底,子组件的渲染取决于父组件传递的数据;
slot的设计思想是传递DOM节点,将父组件的模板代码节点直接传递给子组件的某个slot,来达到最终渲染的目的;
区别二:作用范围
父组件在调用子组件的时候申明并赋值变量,那么子组件内将其加到props列表后,就可以接收并使用,但是一旦传递过来,作用域就发生变化;
子组件虽然为父组件预留slot,但是slot的作用域依然属于父组件,所以可以访问到父组件内的所有状态。
致谢: