// app.vue
<template>
<div>
<bb>
<template v-slot:header="user">
{{user}}
</template>
</bb>
</div>
</template>
<script>
import bb from './bb.vue'
export default {
name: "app",
components: {
bb
}
};
</script>
// b.vue
<template>
<div>
<slot name="header" :user="user"></slot>
</div>
</template>
<script>
export default {
name: 'bb',
data() {
return {
user: '王哥'
}
}
}
</script>
app.vue的渲染函数:
var render = function render() {
var _vm = this,
_c = _vm._self._c
return _c(
"div",
[
_c("bb", {
scopedSlots: _vm._u([
{
key: "header",
fn: function (user) {
return [_vm._v("\n " + _vm._s(user) + "\n ")]
},
},
]),
}),
],
1
)
}
可以看到v-slot:header="user"被渲染成一个名为scopedSlots的对象,{{user}}被放入函数中,传入的user就是v-slot:header="user"的value。然后执行_vm._u方法:
function resolveScopedSlots(fns, res,
// the following are added in 2.6
hasDynamicKeys, contentHashKey) {
res = res || { $stable: !hasDynamicKeys };
for (var i = 0; i < fns.length; i++) {
var slot = fns[i];
if (isArray(slot)) {
resolveScopedSlots(slot, res, hasDynamicKeys);
}
else if (slot) {
// marker for reverse proxying v-slot without scope on this.$slots
// @ts-expect-error
if (slot.proxy) {
// @ts-expect-error
slot.fn.proxy = true;
}
res[slot.key] = slot.fn;
}
}
if (contentHashKey) {
res.$key = contentHashKey;
}
return res;
}
该函数返回一个对象: 最后执行_c方法生成bb组件的vnode: 可以看出来插槽传值的原理,其实就是把你传入的值当做函数变量,插槽里面的值就是函数体。当初始化bb.vue组件的时候会执行initRender(vm)方法,该方法主要执行normalizeScopedSlot方法:
for (var key_1 in scopedSlots) {
if (scopedSlots[key_1] && key_1[0] !== '$') {
res[key_1] = normalizeScopedSlot(ownerVm, normalSlots, key_1, scopedSlots[key_1]);
}
}
function normalizeScopedSlot(vm, normalSlots, key, fn) {
var normalized = function () {
var cur = currentInstance;
setCurrentInstance(vm);
var res = arguments.length ? fn.apply(null, arguments) : fn({});
res =
res && typeof res === 'object' && !isArray(res)
? [res] // single vnode
: normalizeChildren(res);
var vnode = res && res[0];
setCurrentInstance(cur);
return res &&
(!vnode ||
(res.length === 1 && vnode.isComment && !isAsyncPlaceholder(vnode))) // #9658, #10391
? undefined
: res;
};
// this is a slot using the new v-slot syntax without scope. although it is
// compiled as a scoped slot, render fn users would expect it to be present
// on this.$slots because the usage is semantically a normal slot.
if (fn.proxy) {
Object.defineProperty(normalSlots, key, {
get: normalized,
enumerable: true,
configurable: true
});
}
return normalized;
}
该方法是个闭包,返回一个函数,这个函数我们后面会用到。然后赋值给bb组件的vnode。
我们继续看bb.vue组件的渲染函数:
var render = function render() {
var _vm = this,
_c = _vm._self._c
return _c("div", [_vm._t("header", null, { user: _vm.user })], 2)
}
可以看到name="header"被当做_t函数的参数传入,:user="user"作为第三个参数传入。继续看看_t函数:
function renderSlot(name, fallbackRender, props, bindObject) {
var scopedSlotFn = this.$scopedSlots[name];
var nodes;
if (scopedSlotFn) {
// scoped slot
props = props || {};
if (bindObject) {
if (!isObject(bindObject)) {
warn$2('slot v-bind without argument expects an Object', this);
}
props = extend(extend({}, bindObject), props);
}
nodes =
scopedSlotFn(props) ||
(isFunction(fallbackRender) ? fallbackRender() : fallbackRender);
}
else {
nodes =
this.$slots[name] ||
(isFunction(fallbackRender) ? fallbackRender() : fallbackRender);
}
var target = props && props.slot;
if (target) {
return this.$createElement('template', { slot: target }, nodes);
}
else {
return nodes;
}
}
该函数主要执行scopedSlotFn(props)方法:
function normalizeScopedSlot(vm, normalSlots, key, fn) {
var normalized = function () {
var cur = currentInstance;
setCurrentInstance(vm);
var res = arguments.length ? fn.apply(null, arguments) : fn({});
res =
res && typeof res === 'object' && !isArray(res)
? [res] // single vnode
: normalizeChildren(res);
var vnode = res && res[0];
setCurrentInstance(cur);
return res &&
(!vnode ||
(res.length === 1 && vnode.isComment && !isAsyncPlaceholder(vnode))) // #9658, #10391
? undefined
: res;
};
...
}
这里的fn如下图: 这里会执行该函数并传入需要的参数user然后返回vnode。从这里我们可以看到,slot的值取决于传入的props和fn的返回值。
总结
我们在组件里面写的<template v-slot:header="user"> {{user}} </template> 会被渲染成一个对象,该对象里面的参数就是header后面的值,template里面的表达式就是函数返回值,header会被当做函数的key。当渲染到<slot name="header" :user="user"></slot> 时,会先根据name找到key为header函数,然后将props的值{user:'王哥'} 当做参数传给该函数并执行返回一个vnode。
|