这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战
作用域插槽
我们可以利用作用域插槽让父组件的插槽内容访问到子组件的数据,具体的用法是在子组件中以属性的方式记录在子组件中,父组件通过v-slot:[name]=[props]的形式拿到子组件传递的值。子组件<slot>元素上的特性称为插槽Props,另外,vue2.6以后的版本已经弃用了slot-scoped,采用v-slot代替。
var child = {
template: `<div><slot :user="user"></div>`,
data() {
return {
user: {
firstname: 'test'
}
}
}
}
var vm = new Vue({
el: '#app',
components: {
child
},
template: `<div id="app"><child><template v-slot:default="slotProps">{{slotProps.user.firstname}}</template></child></div>`
})
作用域插槽和具名插槽的原理类似,我们接着往下看。
父组件编译阶段
作用域插槽和具名插槽在父组件的用法基本相同,区别在于v-slot定义了一个插槽props的名字,参考对于具名插槽的分析,生成render函数阶段fn函数会携带props参数传入。即:
with(this){return _c('div',{attrs:{"id":"app"}},[_c('child',
{scopedSlots:_u([{key:"default",fn:function(slotProps){return
[_v(_s(slotProps.user.firstname))]}}])})],1)}
子组件渲染
在子组件编译阶段,:user="user"会以属性的形式解析,最终在render函数生成阶段以对象参数的形式传递_t函数。
with(this){return _c('div',[_t("default",null,{"user":user})],2)}
子组件渲染Vnode阶段,根据前面分析会执行renderSlot函数,这个函数前面分析过,对于作用域插槽的处理,集中体现在函数传入的第三个参数。
// 渲染slot组件vnode
function renderSlot(
name,
fallback,
props, // 子传给父的值 { user: user }
bindObject
) {
// scopedSlotFn拿到父组件插槽的执行函数,默认slotname为default
var scopedSlotFn = this.$scopedSlots[name];
var nodes;
// 具名插槽分支
if (scopedSlotFn) { // scoped slot
props = props || {};
if (bindObject) {
if (!isObject(bindObject)) {
warn(
'slot v-bind without argument expects an Object',
this
);
}
// 合并props
props = extend(extend({}, bindObject), props);
}
// 执行时将子组件传递给父组件的值传入fn
nodes = scopedSlotFn(props) || fallback;
}
最终将子组件的插槽props作为参数传递给执行函数执行。回过头看看为什么具名插槽是函数的形式执行而不是直接返回结果。学完作用域插槽我们发现这就是设计巧妙的地方,函数的形式让执行过程更加灵活,作用域插槽只需要以参数的形式将插槽props传入便可以得到想要的结果。