前情提要

本文主要内容

初始化组件

(1).setupComponent

function setupComponent(instance) {
//获取vnode的props(真正传递的props)
const { props, children } = instance.vnode;
//判断当前是否是有状态组件组件
const isStateful = isStatefulComponent(instance);
//通过传递的真实props和声明的props 分离组件参数
//组件参数放入props中 其余放入instance.attrs
//处理了props的default情况等
initProps(instance, props, isStateful);
//初始化插槽
initSlots(instance, children);
//验证名称是否合法,components中的组件名称是否
//合法,代理instance.ctx,创建setup函数的ctx,调用setup函数
//处理得到的结果
const setupResult = isStateful ?
setupStatefulComponent(instance) : undefined;
return setupResult;
}
function isStatefulComponent(instance) {
return instance.vnode.shapeFlag &
ShapeFlags.STATEFUL_COMPONENT;
}

(2).initProps

function initProps(instance, rawProps, isStateful) {
//定义需要放入的
const props = {};
const attrs = {};
//attrs.__vInternal = 1
shared.def(attrs, InternalObjectKey, 1);
//创建propsDefaults
instance.propsDefaults = Object.create(null);
//将真实传递的props分配给instance的props和attrs
setFullProps(instance, rawProps, props, attrs);
//遍历normalized(合并和的props)
for (const key in instance.propsOptions[0]) {
if (!(key in props)) {
props[key] = undefined;
}
}
//最后将分配好的props和attrs赋值到instance
if (isStateful) {
instance.props = reactivity.shallowReactive(props);
} else {
//不存在type.props则让props为attrs
if (!instance.type.props) {
instance.props = attrs;
} else {
instance.props = props;
}
}
instance.attrs = attrs;
}
function setFullProps(instance, rawProps, props, attrs) {
//获取通过mixins和extends合并的props
const [options, needCastKeys] = instance.propsOptions;
let hasAttrsChanged = false; //attrs是否发生改变
let rawCastValues;
if (rawProps) {
for (let key in rawProps) {
//如果key是"ref" "key" "ref_for" "ref_key"
//"onVnodeBeforeMount" "onVnodeMounted"
//"onVnodeBeforeUpdate "onVnodeUpdated"
//"onVnodeBeforeUnmount" "onVnodeUnmounted"
//那么就跳过
if (shared.isReservedProp(key)) {
continue;
}
//获取rawProps:{a:1}=>value=1
const value = rawProps[key];
let camelKey; //小驼峰式的key
if (
options &&
shared.hasOwn(options, (camelKey = shared.camelize(key)))
) {
//这个key不是含有default属性的
if (!needCastKeys || !needCastKeys.includes(camelKey)) {
props[camelKey] = value;
}
//props:{"msg":{default:"a"}}
//含有default属性的放入rawCastValues中
else {
(rawCastValues || (rawCastValues = {}))[camelKey] = value;
}
}
//判断当前的key是否是用于emits的
else if (!isEmitListener(instance.emitsOptions, key)) {
//不是emit自定义事件的key也不是组件参数那么就是attrs
if (!(key in attrs) || value !== attrs[key]) {
attrs[key] = value;
hasAttrsChanged = true;
}
}
}
}
/**
*
* 这里涉及到四个属性instance, rawProps, props, attrs
* instance:是当前组件的实例
* rawProps:真正传递的props可能含有组件参数props,
* 标签属性attrs,自定义emit事件
* props:代表声明并且接受到的props
* attrs:代表没有声明props也不属于emits属性的属性
* needCastKeys:代表需要特殊处理的属性
* 例如props:{msg:{default:"a"}}那么msg会被放入
* needCastKeys中
*
*/
if (needCastKeys) {
//获取非响应式的props
const rawCurrentProps = reactivity.toRaw(props);
const castValues = rawCastValues || {};
for (let i = 0; i < needCastKeys.length; i++) {
const key = needCastKeys[i]; //msg
//对于有default的属性进行重设
//props:{msg:{default:"a"}}
props[key] = resolvePropValue(
options, //合并mixins和extends后的props(定义方)
rawCurrentProps, //非响应式的props(接受方)
key, //(含有default)的key "msg"
//例如传递了"msg":1 定义了:props:{msg:{default:"a"}}
//castValues[key]=1
castValues[key],
instance, //实例
!shared.hasOwn(castValues, key)
);
}
}
return hasAttrsChanged;
}
function resolvePropValue(options, props, key, value, instance, isAbsent) {
//获取{msg:{default:"a"}}中的{default:"a"}
const opt = options[key];
if (opt != null) {
//判断是否有default属性
const hasDefault = shared.hasOwn(opt, "default");
//如果定义了default但是没有接受到value值
if (hasDefault && value === undefined) {
const defaultValue = opt.default;
//如果需要接受的类型不是函数,但是接受到了函数
//看看实例的propsDefaults是否有当前key的值
//还是没有则调用这个defaultValue函数取得值
if (opt.type !== Function && shared.isFunction(defaultValue)) {
const { propsDefaults } = instance;
if (key in propsDefaults) {
value = propsDefaults[key];
} else {
//包裹是为了在调用这个函数的时候
//获取当前实例不会出错
setCurrentInstance(instance);
value = propsDefaults[key] = defaultValue.call(null, props);
unsetCurrentInstance();
}
}
//设置为默认值
else {
value = defaultValue;
}
}
//需要接受的类型是Boolean
if (opt[0]) {
//没有设置默认值,也没有传递这个值则为false
if (isAbsent && !hasDefault) {
value = false;
}
//<Comp yes></Comp>并且声明了yes则设置为true
else if (opt[1] && value === "") {
value = true;
}
}
}
return value;
}

(3).initSlots

function initSlots(instance, children) {
//判断当前实例的children是否是slots
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
const type = children._; //获取shapeSlots
//有"_"标识表示是通过compiler编译得到的
if (type) {
//如果有SLOTS_CHILDREN标识 表示children就是slots属性
instance.slots = reactivity.toRaw(children);
//将_属性变为不可枚举属性
shared.def(children, "_", type);
} else {
/**
* render(){
*   return h(Comp,null,{
*     default:()=>h("div",null),
*     header:()=>h("div",null)
*   })
* }
* 没有则表示用户自己写了render函数
* 这个时候用户可能不会添加"_"属性
* 所以需要对slots进行标准化
*/
normalizeObjectSlots(children, (instance.slots = {}));
}
} else {
instance.slots = {};
//如果children为字符串或者null或数组情况
if (children) {
normalizeVNodeSlots(instance, children);
}
}
//标识slots为内部属性
shared.def(instance.slots, InternalObjectKey, 1);
}
<template>
<Comp>
我是插槽内容
</Comp>
</template>
//编译后
function render(_ctx, _cache) {
const _component_Comp = _resolveComponent("Comp", true)
return (_openBlock(),
_createBlock(_component_Comp, null, {
default: _withCtx(() => [
_createTextVNode(" 我是插槽内容 ")
]),
_: 1 /* STABLE */
}))
}
const normalizeObjectSlots = (rawSlots, slots, instance) => {
const ctx = rawSlots._ctx;
for (const key in rawSlots) {
//_开头或者$stable跳过
//这将允许设置不进行标准化的插槽
if (isInternalKey(key)) continue;
//获取slots的值
const value = rawSlots[key];
//如果value已经是一个函数了,需要包裹withCtx执行
//进行标准化 都需要改成通过编译的样子
if (shared.isFunction(value)) {
//给instance.slots赋值
slots[key] = normalizeSlot(key, value, ctx);
}
/**
* 用户不写函数,抛出警告,使用函数的性能将会更好
* render(){
*   return createVnode(Comp,null,{
*      default:createVnode('div',null)
*   })
* }
*/
else if (value != null) {
console.warn(
`Non-function value encountered for slot "${key}". ` +
`Prefer function slots for better performance.`
);
//经过normalizeSlotValue处理 返回的createVnode一定通过数组包裹
const normalized = normalizeSlotValue(value);
slots[key] = () => normalized;
}
}
};
const normalizeSlot = (key, rawSlot, ctx) => {
//已经经过标准化的slot不需要在进行标准化
if (rawSlot._n) {
return rawSlot;
}
const normalized = withCtx((...args) => {
if (getCurrentInstance()) {
warn(
`Slot "${key}" invoked outside of the render function: ` +
`this will not track dependencies used in the slot. ` +
`Invoke the slot function inside the render function instead.`
);
}
//标准化插槽的值 rawSlot=> default:()=>createVnode('div',null)
return normalizeSlotValue(rawSlot(...args));
}, ctx);
//表示不是经过compiler编译的,是用户自己写的render函数
normalized._c = false;
return normalized;
};
function withCtx(
fn,
ctx = getCurrentRenderingInstance(),
isNonScopedSlot
) {
if (!ctx) return fn;
if (fn._n) {
return fn;
}
//设置currentRenderingInstance,通过闭包确保调用fn的时候
//currentRenderingInstance实例为当前实例
/**
* 如果用户调用模板表达式内的插槽
*  <Button>
*    <template>
*      <slot></slot>
*    </template>
*  </Button>
* 可能会扰乱块跟踪,因此默认情况下,禁止块跟踪,当
* 调用已经编译的插槽时强制跳出(由.d标志指示)。
* 如果渲染已编译的slot则无需执行此操作、因此
* 我们在renderSlot中调用renderFnWithContext
* 时,.d设置为false
*/
const renderFnWithContext = (...args) => {
//禁止块追踪,将isBlockTreeEnabled设置为0将会停止追踪
if (renderFnWithContext._d) {
setBlockTracking(-1);
}
const prevInstance = setCurrentRenderingInstance(ctx);
const res = fn(...args);
setCurrentRenderingInstance(prevInstance);
//开启块追踪
if (renderFnWithContext._d) {
setBlockTracking(1);
}
return res;
};
//如果已经是renderFnWithContext则不需要在包装了
renderFnWithContext._n = true; //_n表示已经经过renderFnWithContext包装
renderFnWithContext._c = true; //表示经过compiler编译得到
//true代表禁止块追踪,false代表开启块追踪
renderFnWithContext._d = true;
return renderFnWithContext;
}
function normalizeSlotValue(value){
if(shared.isArray(value)){
return value.map(normalizeVNode)
}
return [normalizeVNode(value)]
}
//自己写render函数
export default {
render(){
return createVNode(Comp,null,{
default:()=>([
createVNode('div',null),
createVNode('div',null)
])
})
}
}
//如果是正常编译获得的那么应该是
export default{
render(){
return createVNode(Comp,null,{
default:()=>createTextVNode('123')
})
}
}
//自己写render函数
export default {
render(){
return createVNode(Comp,null,{
default:()=>123
})
}
}
function normalizeVNode(child) {
if (child == null || typeof child === "boolean") {
//没有child或者没有实质性内容创建注释节点
return createVNode(Comment);
} else if (shared.isArray(child)) {
//用户直接写了一个数组,需要包裹一层Fragment
return createVNode(Fragment, null, child.slice());
}
//如果这个节点已经挂载过了克隆这个节点(复用节点)
else if (typeof child === "object") {
return cloneIfMounted(child);
}
//string 或者 number
else {
return createVNode(Text, null, String(child));
}
}
render(){
return createVNode(Comp,null,{
default:createVNode('div')
})
}
//经过标准化后,相当于
render(){
return createVNode(Comp,null,{
default:withCtx(()=>[createVNode('div')])
})
}
//其他的情况都差不多,都是为了标准化为
//满足上面四个条件的样子
const normalizeVNodeSlots = (instance, children) => {
const normalized = normalizeSlotValue(children);
instance.slots.default = () => normalized;
};

额外内容

//挂载过的vnode有el属性
function cloneIfMounted(child) {
return child.el === null || child.memo ?
child : cloneVNode(child);
}
function mergeProps(...args) {
const ret = {};
for (let i = 0; i < args.length; i++) {
const toMerge = args[i];
for (const key in toMerge) {
//结合class
if (key === "class") {
if (ret.class !== toMerge.class) {
ret.class = shared.normalizeClass([ret.class, toMerge.class]);
}
}
//结合style属性
else if (key === "style") {
ret.style = shared.normalizeStyle([ret.style, toMerge.style]);
}
else if (key !== "") {
ret[key] = toMerge[key];
}
}
}
return ret;
}
function cloneVNode(vnode, extraProps, mergeRef = false) {
const { props, ref, patchFlag, children } = vnode;
const mergedProps = extraProps ? mergeProps(props || {}, extraProps) : props;
const cloned = {
//省略了大量属性,其他的属性和传递的
//vnode一样,这里只列举了可能被改变的
key: mergedProps && normalizeKey(mergedProps),
ref:
extraProps && extraProps.ref
? mergeRef && ref
? shared.isArray(ref)
? ref.concat(normalizeRef(extraProps))
: [ref, normalizeRef(extraProps)]
: normalizeRef(extraProps)
: ref,
children:
patchFlag === PatchFlags.HOISTED && shared.isArray(children)
? children.map(deepCloneVNode)
: children,
shapeFlag: vnode.shapeFlag,
patchFlag:
extraProps && vnode.type !== Fragment
? patchFlag === PatchFlags.HOISTED
? PatchFlags.FULL_PROPS
: patchFlag | PatchFlags.FULL_PROPS
: patchFlag,
};
return cloned;
}
function deepCloneVNode(vnode) {
const cloned = cloneVNode(vnode);
if (shared.isArray(vnode.children)) {
cloned.children = vnode.children.map(deepCloneVNode);
}
return cloned;
}

总结

以上就是Vue3源码分析组件挂载初始化props与slots的详细内容,更多关于Vue3组件挂载初始化的资料请关注本站其它相关文章!

发表回复