深入浅析Vue中的slots/scoped slots

2020-06-16 06:21:26易采站长站整理

*当需要向子组件的子组件传递具名插槽时,不会保持插槽的名字。
* 举个栗子:
* child组件template:
* <div>
* <div class="default"><slot></slot></div>
* <div class="named"><slot name="foo"></slot></div>
* </div>
* parent组件template:
* <child><slot name="foo"></slot></child>
* main组件template:
* <parent><span slot="foo">foo</span></parent>
* 此时main渲染的结果:
* <div>
* <div class="default"><span slot="foo">foo</span></div>
<div class="named"></div>
* </div>
*/
if ((child.context === context || child.fnContext === context) &&
data && data.slot != null
) {
const name = data.slot
const slot = (slots[name] || (slots[name] = []))
// 这里处理父组件采用template形式的插槽
if (child.tag === 'template') {
slot.push.apply(slot, child.children || [])
} else {
slot.push(child)
}
} else {
// 返回匿名default插槽VNode数组
(slots.default || (slots.default = [])).push(child)
}
}
// 忽略仅仅包含whitespace的插槽
for (const name in slots) {
if (slots[name].every(isWhitespace)) {
delete slots[name] }
}
return slots
}

然后挂载dx-li组件时,会调用dx-li组件render函数,在此过程中会调用renderSlot函数:


export function renderSlot (
name: string, // 子组件中slot的name,匿名default
fallback: ?Array<VNode>, // 子组件插槽中默认内容VNode数组,如果没有插槽内容,则显示该内容
props: ?Object, // 子组件传递到插槽的props
bindObject: ?Object // 针对<slot v-bind="obj"></slot> obj必须是一个对象
): ?Array<VNode> {
// 判断父组件是否传递作用域插槽
const scopedSlotFn = this.$scopedSlots[name] let nodes
if (scopedSlotFn) { // scoped slot
props = props || {}
if (bindObject) {
if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) {
warn(
'slot v-bind without argument expects an Object',
this
)
}
props = extend(extend({}, bindObject), props)
}
// 传入props生成相应的VNode
nodes = scopedSlotFn(props) || fallback
} else {
// 如果父组件没有传递作用域插槽
const slotNodes = this.$slots[name] // warn duplicate slot usage
if (slotNodes) {
if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) {
warn(
`Duplicate presence of slot "${name}" found in the same render tree ` +
`- this will likely cause render errors.`,
this