大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
一、概念
- // 有name的属于具名插槽,没有name属于匿名插槽
- <app>
- <div slot=“a”>xxxx</div>
- <div slot=“b”>xxxx</div>
- </app>
- <slot name=“a”></slot>
- <slot name=“b”></slot>
-
- 普通插槽渲染的位置是在它的父组件里面,而不是在子组件里面
- 作用域插槽渲染是在子组件里面
1.插槽slot
在渲染父组件的时候,会将插槽中的先渲染。
创建组件虚拟节点时,会将组件的儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿 子进行分类 {a:[vnode],b[vnode]}
渲染组件时会拿对应的slot属性的节点进行替换操作。(插槽的作用域为父组件,插槽中HTML模板显示不显示、以及怎样显示由父组件来决定)
有name的父组件通过html模板上的slot属性关联具名插槽。没有slot属性的html模板默认关联匿名插槽。
2.作用域插槽slot-scope
作用域插槽在解析的时候,不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。
或者可以说成作用域插槽是子组件可以在slot标签上绑定属性值,在父组件可以拿到子组件的数据,通过子组件绑定数据传递给父组件。(插槽的作用域为子组件)
- 子组件:
- <slot :nickName=
“‘wthreesix'”></slot> -
- 父组件:
- <slot-child>
- <template slot-scope=“scope”>
- <div>{{scope.nickName}}</div>
- </template>
- </slot-child>
二、源码图
三、插槽渲染分析
1.插槽
- // 父组件:
- const VueTemplateCompiler = require(‘vue-template-compiler’);
- let ele = VueTemplateCompiler.compile(`
- <my-component>
- <div slot=”header”>node</div>
- <div>react</div>
- <div slot=”footer”>vue</div>
- </my-component>
- `)
-
- 将上面组件编译后:
- /**
- 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(`
- <div>
- <slot name=”header”></slot>
- <slot name=”footer”></slot>
- <slot></slot>
- </div>
- `);
- /**
- 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(`
- <app>
- <div slot-scope=”msg” slot=”footer”>{{msg.a}}</div>
- </app>
- `);
- /**
- with(this) {
- // 编译出的不是一个child,而是一个属性scopedSlots
- return _c(‘app’, {
- scopedSlots: _u([{
- key: “footer”,
- fn: function (msg) { 将子节点变成一个函数,这个函数不调用就不会去渲染这个子节点
- return _c(‘div’, {}, [_v(_s(msg.a))])
- }
- }])
- })
- }
- }
- 在初始化的时候并不会渲染子节点
- */
-
- 子组件:
- const VueTemplateCompiler = require(‘vue-template-compiler’);
- VueTemplateCompiler.compile(
` - // 当我们在写slot去执行的时候进行渲染
- <div>
- <slot name=”footer” a=”1″ b=”2″></slot>
- </div>
- `);
- /**
- with(this) {
- // 去找footer,找见会调用上面的那个函数,并且将属性传入到这个函数里面,这时候才会把这节点进行渲染,完成之后替换调
- return _c(‘div’, [_t(“footer”, null, {
- “a”: “1”,
- “b”: “2”
- })], 2)
- }
- **/
-
- // 作用域插槽的内容会被渲染成一个函数
- // 作用域插槽渲染是在当前组件的内部,不是在父组件中
四、源码
1.initRender(初始化render,构建vm.$slots)
- export function initRender (vm: Component) {
- vm.$slots = resolveSlots(options._renderChildren, renderContext)
- vm.$scopedSlots = emptyObject
- }
2.resolveSlots(映射slot名字和对应的vnode)
- export function resolveSlots (
- children: ?Array<VNode>,
- context: ?Component
- ): { [key: string]: Array<VNode> } {
- 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<VNode> },
- 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用函数的返回值进行渲染。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/168374.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...