一、概念
-
// 有name的属于具名插槽,没有name属于匿名插槽
-
-
-
-
-
-
-
-
普通插槽渲染的位置是在它的父组件里面,而不是在子组件里面
-
1.插槽slot
在渲染父组件的时候,会将插槽中的先渲染。
创建组件虚拟节点时,会将组件的儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿 子进行分类 {a:[vnode],b[vnode]}
渲染组件时会拿对应的slot属性的节点进行替换操作。(插槽的作用域为父组件,插槽中HTML模板显示不显示、以及怎样显示由父组件来决定)
有name的父组件通过html模板上的slot属性关联具名插槽。没有slot属性的html模板默认关联匿名插槽。
2.作用域插槽slot-scope
作用域插槽在解析的时候,不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。
或者可以说成作用域插槽是子组件可以在slot标签上绑定属性值,在父组件可以拿到子组件的数据,通过子组件绑定数据传递给父组件。(插槽的作用域为子组件)
-
-
<slot :nickName="‘wthreesix‘"></slot>
-
-
-
-
<template slot-scope="scope">
-
<div>{{scope.nickName}}</div>
-
-
二、源码图

三、插槽渲染分析
1.插槽
-
-
const VueTemplateCompiler = require(‘vue-template-compiler‘);
-
let ele = VueTemplateCompiler.compile(`
-
-
<div slot="header">node</div>
-
-
<div slot="footer">vue</div>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
let ele = VueTemplateCompiler.compile(`
-
-
<slot name="header"></slot>
-
<slot name="footer"></slot>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
插槽就是一个替换的过程,将父组件渲染好的结果直接替换到自己的上面,创建的过程相当于在父组件渲染的
2.作用域插槽
-
-
let ele = VueTemplateCompiler.compile(`
-
-
<div slot-scope="msg" slot="footer">{{msg.a}}</div>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
const VueTemplateCompiler = require(‘vue-template-compiler‘);
-
VueTemplateCompiler.compile(`
-
-
-
<slot name="footer" a="1" b="2"></slot>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
四、源码
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 (
-
-
-
): { [key: string]: Array<VNode> } {
-
if (!children || !children.length) {
-
-
-
-
for (let i = 0, l = children.length; i < l; i++) {
-
const child = children[i]
-
-
-
if (data && data.attrs && data.attrs.slot) {
-
-
-
-
-
if ((child.context === context || child.fnContext === context) &&
-
data && data.slot != null
-
-
-
const slot = (slots[name] || (slots[name] = []))
-
if (child.tag === ‘template‘) {
-
slot.push.apply(slot, child.children || [])
-
-
-
-
-
(slots.default || (slots.default = [])).push(child)
-
-
-
-
for (const name in slots) {
-
if (slots[name].every(isWhitespace)) {
-
-
-
-
-
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
-
-
-
const hasNormalSlots = Object.keys(normalSlots).length > 0
-
const isStable = slots ? !!slots.$stable : !hasNormalSlots
-
const key = slots && slots.$key
-
-
-
} else if (slots._normalized) {
-
-
-
-
-
-
prevSlots !== emptyObject &&
-
key === prevSlots.$key &&
-
-
-
-
-
-
-
-
-
for (const key in slots) {
-
if (slots[key] && key[0] !== ‘$‘) {
-
res[key] = normalizeScopedSlot(normalSlots, key, slots[key])
-
-
-
-
-
for (const key in normalSlots) {
-
-
res[key] = proxyNormalSlot(normalSlots, key)
-
-
-
-
-
if (slots && Object.isExtensible(slots)) {
-
(slots: any)._normalized = res
-
-
def(res, ‘$stable‘, isStable)
-
-
def(res, ‘$hasNormal‘, hasNormalSlots)
-
-
4.proxyNormalSlot(将slot代理到scopeSlots上)
-
function proxyNormalSlot(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.length === 1 && res[0].isComment)
-
-
-
-
-
-
-
-
Object.defineProperty(normalSlots, key, {
-
-
-
-
-
-
-
6.最后调用renderSlot用函数的返回值进行渲染。
什么是作用域插槽?插槽与作用域插槽的区别
原文:https://www.cnblogs.com/1549983239yifeng/p/14277604.html