Vue源码系列——patch做了什么

720 阅读2分钟

概述

触发依赖之后都会执行patch函数,它做了什么呢?

patch的作用是通过对比新旧虚拟dom,来更新dom。其中涉及到的知识点非常多,我们必须一步一步的分析。

h函数的使用

const h = (type, props, children) => {
    return createVNode(type, props, children);
};
// 使用
h("div", {cc: 1132}, [
     h("p", {aaa: 111}, "apiInject"), 
     h(ProviderOne, {aaa: 222})
 ]);
 h(ProviderTwo); // ProviderTwo是一个组件

通过h函数的使用分析createVNode的三个参数:

type:1、html标签;2、一个组件。

props:是属性。1、如果type是标签,则props是标签的属性;2、如果type是组件,则props可以在其setup中接收;3、没有则是undefined。

children:1、字符串:文本子元素内容;2、数组:多个子元素,可以在里面用h函数。

createVNode:h函数的参数 => VNode

const createVNode = function (type, props, children) {
    const vnode = {
        el: null,
        component: null,
        key: props === null || props === void 0 ? void 0 : props.key,
        type,
        props: props || {},
        children,
        shapeFlag: getShapeFlag(type),
    };
    if (Array.isArray(children)) {
        vnode.shapeFlag |= 16;
    }
    else if (typeof children === "string") {
        vnode.shapeFlag |= 8;
    }
    normalizeChildren(vnode, children);
    return vnode;
};

三个参数生产vnode

const vnode = {
    el: null,
    component: null,
    key: props === null || props === void 0 ? void 0 : props.key,
    type,
    props: props || {},
    children,
    shapeFlag: getShapeFlag(type),
};

生成key

key:props中有key则用,无key则设为0;

生成shapeFlag

shapeFlag:设置一个标记:string:1;array:4

function getShapeFlag(type) {
    return typeof type === "string"
        ? 1
        : 4;
}

产生四种标记

  1. type是string(标签)、children是array:shapeFlag = 17
  2. type是string(标签 、children是string:shapeFlag = 9
  3. type是组件、children是array:shapeFlag = 20
  4. type是组件、children是string:shapeFlag = 12
if (Array.isArray(children)) {
    vnode.shapeFlag |= 16;
} else if (typeof children === "string") {
    vnode.shapeFlag |= 8;
}

执行normalizeChildren

满足typeof children === "object" shapeFlag分别是17、20

  1. 17 & 1 = 1
  2. 20 & 1 = 0 执行vnode.shapeFlag |= 32:shapeFlag=52
function normalizeChildren(vnode, children) {
    if (typeof children === "object") {
        if (vnode.shapeFlag & 1) ;
        else {
            vnode.shapeFlag |= 32;
        }
    }
}

最终结果

type\childrenstringarrayundefined
标签string9171
组件object12524

第一次调用patch

const render = (vnode, container) => {
    patch(null, vnode, container);
};

patch源码

function patch(n1, n2, container = null, parentComponent = null) {
        const { type, shapeFlag } = n2;
        switch (type) {
            case Text:
                processText(n1, n2, container);
                break;
            case Fragment:
                processFragment(n1, n2, container);
                break;
            default:
                if (shapeFlag & 1) {
                    // console.log("处理 element");
                    processElement(n1, n2, container);
                }
                else if (shapeFlag & 4) {
                    // console.log("处理 component");
                    processComponent(n1, n2, container, parentComponent);
                }
        }
    }

type:组件对象、标签名,所以先不考虑Text、Fragment

结合上面的表格,得出结论

  1. 17、1、9(type是标签)会执行processElement(n1, n2, container)
  2. 4、12、52(type是组件)会执行processComponent(n1, n2, container, parentComponent);

篇幅太长,处理标签和组件单独写文章吧