processComponent处理组件
function processComponent(n1, n2, container, parentComponent) {
if (!n1) {
mountComponent(n2, container, parentComponent);
}
else {
updateComponent(n1, n2);
}
}
mountComponent挂载组件
function mountComponent(initialVNode, container, parentComponent) {
const instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent));=
setupComponent(instance);
setupRenderEffect(instance, initialVNode, container);
}
通过createComponentInstance生成实例
- 有父组件的赋值给provides
- isMounted设置为false
- ctx设置未instance(实例自身)
- 给emit设置this指向
- 把实例instance挂给初始VNode.component
function createComponentInstance(vnode, parent) {
const instance = {
type: vnode.type,
vnode,
next: null,
props: {},
parent,
provides: parent ? parent.provides : {},
proxy: null,
isMounted: false,
attrs: {},
slots: {},
ctx: {},
setupState: {},
emit: () => { },
};
instance.ctx = {
_: instance,
};
instance.emit = emit.bind(null, instance);
return instance;
}
setupComponent
- initProps:把vNode的props挂在实例instance上
- initSlots:当type是object组件、children是数组array时,normalizeObjectSlots函数会把childred中所有的函数保存在instance.slots中
function setupComponent(instance) {
const { props, children } = instance.vnode;
initProps(instance, props);
initSlots(instance, children);
setupStatefulComponent(instance);
}
function initProps(instance, rawProps) {
instance.props = rawProps;
}
function initSlots(instance, children) {
const { vnode } = instance;
if (vnode.shapeFlag & 32) {
normalizeObjectSlots(children, (instance.slots = {}));
}
}
const normalizeObjectSlots = (rawSlots, slots) => {
for (const key in rawSlots) {
const value = rawSlots[key];
if (typeof value === "function") {
slots[key] = (props) => normalizeSlotValue(value(props));
}
}
};
function setupStatefulComponent(instance) {
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);
const Component = instance.type;
const { setup } = Component;
if (setup) {
setCurrentInstance(instance);
const setupContext = createSetupContext(instance);
const setupResult = setup && setup(shallowReadonly(instance.props), setupContext);
setCurrentInstance(null);
handleSetupResult(instance, setupResult);
}
else {
finishComponentSetup(instance);
}
}
setupStatefulComponent比较复杂,一步一步分析
function setupStatefulComponent(instance) {
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);
const Component = instance.type;
const { setup } = Component;
if (setup) {
setCurrentInstance(instance);
const setupContext = createSetupContext(instance);
const setupResult = setup && setup(shallowReadonly(instance.props), setupContext);
setCurrentInstance(null);
handleSetupResult(instance, setupResult);
}
else {
finishComponentSetup(instance);
}
}
代理instance.ctx赋值给instance.proxy
分析PublicInstanceProxyHandlers能知道其作用:获取实例中数据时,先在setupState、props中找,然后再找emit、props。修改的时候只能修改setupState中的数据
instance.ctx = {
_: instance,
};
const publicPropertiesMap = {
$el: (i) => i.vnode.el,
$emit: (i) => i.emit,
$slots: (i) => i.slots,
$props: (i) => i.props,
};
const PublicInstanceProxyHandlers = {
get({ _: instance }, key) {
const { setupState, props } = instance;
if (key !== "$") {
if (hasOwn(setupState, key)) {
return setupState[key];
}
else if (hasOwn(props, key)) {
return props[key];
}
}
const publicGetter = publicPropertiesMap[key];
if (publicGetter) {
return publicGetter(instance);
}
},
set({ _: instance }, key, value) {
const { setupState } = instance;
if (setupState !== {} && hasOwn(setupState, key)) {
setupState[key] = value;
}
},
};
继续分析:
if (setup) {
setCurrentInstance(instance);
const setupContext = createSetupContext(instance);
const setupResult = setup && setup(shallowReadonly(instance.props), setupContext);
setCurrentInstance(null);
handleSetupResult(instance, setupResult);
}
else {
finishComponentSetup(instance);
}
有setup:
- 把instance暂存在外部变量currentInstance,如果用到inject就会使用currentInstance,会有单独的文章讲inject。
- 从instance中拿出attrs、slots、emit、expose存放给setupContext
- 执行setup,两个参数:1、instance.props做只读代理;2、setupContext。执行setup过程中会给所有响应式数据添加依赖。
- currentInstance重置为null
- handleSetupResult执行,两个参数:1、实例;2、setup的返回结果setupResult
- setupResult如果是函数则赋值给render,是对象则代理后复制给setupState
- 执行finishComponentSetup
function setCurrentInstance(instance) {
currentInstance = instance;
}
function createSetupContext(instance) {
return {
attrs: instance.attrs,
slots: instance.slots,
emit: instance.emit,
expose: () => { },
};
}
function handleSetupResult(instance, setupResult) {
if (typeof setupResult === "function") {
instance.render = setupResult;
}
else if (typeof setupResult === "object") {
instance.setupState = proxyRefs(setupResult);
}
finishComponentSetup(instance);
}
const shallowUnwrapHandlers = {
get(target, key, receiver) {
return unRef(Reflect.get(target, key, receiver));
},
set(target, key, value, receiver) {
const oldValue = target[key];
if (isRef(oldValue) && !isRef(value)) {
return (target[key].value = value);
}
else {
return Reflect.set(target, key, value, receiver);
}
},
};
function proxyRefs(objectWithRefs) {
return new Proxy(objectWithRefs, shallowUnwrapHandlers);
}
finishComponentSetup
有无setup,都会执行它。
当setup函数返回的是一个对象时。会把这个对象当作setupState,组件中必须有一个render函数用来渲染。
function finishComponentSetup(instance) {
const Component = instance.type;
if (!instance.render) {
instance.render = Component.render;
}
}
setupRenderEffect渲染
- 未挂载的实例:isMounted设置为true,执行render函数,执行patch处理子节点
- 已挂载的实例:执行render函数,执行patch处理子节点
function setupRenderEffect(instance, initialVNode, container) {
function componentUpdateFn() {
if (!instance.isMounted) {
const proxyToUse = instance.proxy;
const subTree = (instance.subTree = instance.render.call(proxyToUse, proxyToUse));
patch(null, subTree, container, instance);
initialVNode.el = subTree.el;
instance.isMounted = true;
}
else {
const { next, vnode } = instance;
if (next) {
next.el = vnode.el;
updateComponentPreRender(instance, next);
}
const proxyToUse = instance.proxy;
const nextTree = instance.render.call(proxyToUse, proxyToUse);
const prevTree = instance.subTree;
instance.subTree = nextTree;
patch(prevTree, nextTree, prevTree.el, instance);
}
}
instance.update = effect(componentUpdateFn, {
scheduler: () => {
queueJob(instance.update);
},
});
}
updateComponent更新组件
shouldUpdateComponent对比props有没有修改: 有修改则执行update,根据n2更新页面。 没有修改则,把实例更新问n2。
function updateComponent(n1, n2, container) {
const instance = (n2.component = n1.component);
if (shouldUpdateComponent(n1, n2)) {
instance.next = n2;
instance.update();
} else {
n2.component = n1.component;
n2.el = n1.el;
instance.vnode = n2;
}
}