Vue源码系列——patch处理组件

218 阅读3分钟

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生成实例

  1. 有父组件的赋值给provides
  2. isMounted设置为false
  3. ctx设置未instance(实例自身)
  4. 给emit设置this指向
  5. 把实例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

  1. initProps:把vNode的props挂在实例instance上
  2. 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中找,然后再找elel、emit、slotsslots、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:

  1. 把instance暂存在外部变量currentInstance,如果用到inject就会使用currentInstance,会有单独的文章讲inject。
  2. 从instance中拿出attrs、slots、emit、expose存放给setupContext
  3. 执行setup,两个参数:1、instance.props做只读代理;2、setupContext。执行setup过程中会给所有响应式数据添加依赖。
  4. currentInstance重置为null
  5. handleSetupResult执行,两个参数:1、实例;2、setup的返回结果setupResult
  6. setupResult如果是函数则赋值给render,是对象则代理后复制给setupState
  7. 执行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渲染

  1. 未挂载的实例:isMounted设置为true,执行render函数,执行patch处理子节点
  2. 已挂载的实例:执行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;
    }
}

所有的组件处理,到内部都是处理元素标签,看下篇吧