什么是作用域插槽?插槽与作用域插槽的区别

什么是作用域插槽?插槽与作用域插槽的区别创建组件虚拟节点时 会将组件的儿子的虚拟节点保存起来 当初始化组件时 通过插槽属性将儿 子进行分类 a vnode b vnode 渲染组件时会拿对应的 slot 属性的节点进行替换操作 插槽的作用域为父组件 插槽中 HTML 模板显示不显示 以及怎样显示由父组件来决定 有 name 的父组件通过 html 模板上的 slot 属性关联具名插槽 没有 slot 属性的 html 模板默认关联匿名插槽 2

创建组件虚拟节点时,会将组件的儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿 子进行分类 {a:[vnode],b[vnode]}

渲染组件时会拿对应的slot属性的节点进行替换操作。(插槽的作用域为父组件,插槽中HTML模板显示不显示、以及怎样显示由父组件来决定)

有name的父组件通过html模板上的slot属性关联具名插槽。没有slot属性的html模板默认关联匿名插槽。

2.作用域插槽slot-scope

作用域插槽在解析的时候,不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。

或者可以说成作用域插槽是子组件可以在slot标签上绑定属性值,在父组件可以拿到子组件的数据,通过子组件绑定数据传递给父组件。(插槽的作用域为子组件)


子组件:


“‘wthreesix'”>



父组件:







二、源码图

三、插槽渲染分析

1.插槽


// 父组件:


const VueTemplateCompiler = require(‘vue-template-compiler’);


let ele = VueTemplateCompiler.compile(`




node


react


vue




`)



将上面组件编译后:


/**


with(this) {


return _c(‘my-component’, // _c 创建虚拟dom


[ // 组件中包含子节点


_c(‘div’, // 第一个是一个插槽,插槽叫header


{


attrs: {


“slot”: “header”


},


slot: “header”


},


[_v(“node”)] // 将里面内容存起来了(_v是createTextVNode创建文本节点)


),


_v(” “),


_c(‘div’,[_v(“react”)]),


_v(” “),


_c(‘div’, {


attrs: {


“slot”: “footer”


},


slot: “footer”


}, [_v(“vue”)])


]


)


}


*/


在调render方法的时候已经将组件全部渲染好了



// 子组件


let ele = VueTemplateCompiler.compile(`












`);


/**


with(this) {


// 渲染的时候会找header对应的是谁


return _c(‘div’, [


_t(“header”), _v(” “),


_t(“footer”), _v(” “),


_t(“default”)], 2)


}


当找到就会换过来,如下:


return _c(‘div’, [_v(“node”), _v(” “), _v(“vue”), _v(” “),


_t(“default”)], 2)


}


}


**/


// _t是renderSlot



插槽就是一个替换的过程,将父组件渲染好的结果直接替换到自己的上面,创建的过程相当于在父组件渲染的

2.作用域插槽


父组件:


let ele = VueTemplateCompiler.compile(`




{{msg.a}}



/p>

p>


`);


/p>

p>


/**


/p>

p>


with(this) {


/p>

p>


// 编译出的不是一个child,而是一个属性scopedSlots


/p>

p>


return _c(‘app’, {


/p>

p>


scopedSlots: _u([{


/p>

p>


key: “footer”,


/p>

p>


fn: function (msg) { 将子节点变成一个函数,这个函数不调用就不会去渲染这个子节点


/p>

p>


return _c(‘div’, {}, [_v(_s(msg.a))])


/p>

p>


}


/p>

p>


}])


/p>

p>


})


/p>

p>


}


/p>

p>


}


/p>

p>


在初始化的时候并不会渲染子节点


/p>

p>


*/


/p>

p>


/p>

p>


子组件:


/p>

p>


const VueTemplateCompiler = require(‘vue-template-compiler’);


/p>

p>


VueTemplateCompiler.compile(


`


/p>

p>


// 当我们在写slot去执行的时候进行渲染


/p>

p>



/p>

p>



/p>

p>



/p>

p>


`);


/p>

p>


/**


/p>

p>


with(this) {


/p>

p>


// 去找footer,找见会调用上面的那个函数,并且将属性传入到这个函数里面,这时候才会把这节点进行渲染,完成之后替换调


/p>

p>


return _c(‘div’, [_t(“footer”, null, {


/p>

p>


“a”: “1”,


/p>

p>


“b”: “2”


/p>

p>


})], 2)


/p>

p>


}


/p>

p>


**/


/p>

p>


/p>

p>


// 作用域插槽的内容会被渲染成一个函数


/p>

p>


// 作用域插槽渲染是在当前组件的内部,不是在父组件中


/p>

h3>四、源码

/h3>

p>1.initRender(初始化render,构建vm.$slots)

/p>

p>


export function initRender (vm: Component) {


/p>

p>


vm.$slots = resolveSlots(options._renderChildren, renderContext)


/p>

p>


vm.$scopedSlots = emptyObject


/p>

p>


}


/p>

p>2.resolveSlots(映射slot名字和对应的vnode)

/p>

p>


export function resolveSlots (


/p>

p>


children: ?Array

,


context: ?Component


): { [key: string]: Array } {


if (!children || !children.length) {


return {}


}


const slots = {}


for (let i = 0, l = children.length; i < l; i++) {


const child = children[i]


const data = child.data


// remove slot attribute if the node is resolved as a Vue slot node


if (data && data.attrs && data.attrs.slot) {


delete data.attrs.slot


}


// named slots should only be respected if the vnode was rendered in the


// same context.


if ((child.context === context || child.fnContext === context) &&


data && data.slot !=
null


) {


const name = data.slot


const slot = (slots[name] || (slots[name] = []))


if (child.tag === ‘template’) {


slot.push.apply(slot, child.children || [])


}
else {


slot.push(child)


}


}
else {


(slots.default || (slots.default = [])).push(child)


}


}


// ignore slots that contains only whitespace


for (const name in slots) {


if (slots[name].every(isWhitespace)) {


delete slots[name]


}


}


return slots


}

3.normalizeScopedSlots(core/vdom/helpers/normalize-scoped-slot.js)


export function normalizeScopedSlots (


slots: { [key: string]: Function } | void,


normalSlots: { [key: string]: Array },


prevSlots?: { [key: string]: Function } | void


): any {


let res


const hasNormalSlots = Object.keys(normalSlots).length > 0


const isStable = slots ? !!slots.$stable : !hasNormalSlots


const key = slots && slots.$key


if (!slots) {


res = {}


}
else if (slots._normalized) {


// fast path 1: child component re-render only, parent did not change


return slots._normalized


}
else if (


isStable &&


prevSlots &&


prevSlots !== emptyObject &&


key === prevSlots.$key &&


!hasNormalSlots &&


!prevSlots.$hasNormal


) {


// fast path 2: stable scoped slots w/ no normal slots to proxy,


// only need to normalize once


return prevSlots


}
else {


res = {}


for (const key in slots) {


if (slots[key] && key[0] !== ‘$’) {


res[key] = normalizeScopedSlot(normalSlots, key, slots[key])
// 作用域插槽


}


}


}


// expose normal slots on scopedSlots


for (const key in normalSlots) {


if (!(key in res)) {


res[key] = proxyNormalSlot(normalSlots, key)
// 普通插槽


}


}


// avoriaz seems to mock a non-extensible $scopedSlots object


// and when that is passed down this would cause an error


if (slots && Object.isExtensible(slots)) {


(slots: any)._normalized = res


}


def(res,
‘$stable’, isStable)


def(res,
‘$key’, key)


def(res,
‘$hasNormal’, hasNormalSlots)


return res


}

4.proxyNormalSlot(将slot代理到scopeSlots上)


function proxyNormalSlot(slots, key) {


return () => slots[key]


}

5.normalizeScopedSlot(将scopeSlots对应属性和方法挂载到scopeSlots)


function normalizeScopedSlot(normalSlots, key, fn) {


const normalized = function () {


let res = arguments.length ? fn.apply(null, arguments) : fn({})


res = res &&
typeof res === ‘object’ && !Array.isArray(res)


? [res]
// single vnode


: normalizeChildren(res)


return res && (


res.length ===
0 ||


(res.length ===
1 && res[0].isComment) // #9658


) ?
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


}

6.最后调用renderSlot用函数的返回值进行渲染。

编程小号
上一篇 2025-03-17 23:57
下一篇 2025-03-19 11:57

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/hz/128083.html