patch函数介绍
patch函数用来挂载和更新节点,当节点发生改变,对比旧节点与新节点的不同,采用不同的更新方式。
patch通过对vnode的type进行判断当前是什么类型节点。
下面就然我们看看主要判断逻辑:
const { type, shapeFlag } = n2
//根据新节点的 type 类型,调用不同的处理方法;这里大部分方法名都是以 process 开头,意为加工、处理对应元素
switch (type) {
// 文本节点
case Text:
processText(n1, n2, container, anchor)
break
// 注释节点
case Comment:
processCommentNode(n1, n2, container, anchor)
break
// 静态节点
case Static:
if (n1 == null) {
mountStaticNode(n2, container, anchor, isSVG)
} else if (__DEV__) {
patchStaticNode(n1, n2, container, isSVG)
}
break
// 多根节点
case Fragment:
processFragment(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
break
default:
// 元素
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
// 组件
} else if (shapeFlag & ShapeFlags.COMPONENT) {
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
// 传送节点
} else if (shapeFlag & ShapeFlags.TELEPORT) {
;(type as typeof TeleportImpl).process(
n1 as TeleportVNode,
n2 as TeleportVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
// 异步类组件
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).process(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
} else if (__DEV__) {
warn('Invalid VNode type:', type, `(${typeof type})`)
}
}
case 条件不用多说了,主要是default 部分的判断。首先从vnode中结构出shapeFlag,然后利用此属性和其他属性进行与运算。判断当前节点类型,然后进行不同的操作。
shapeFlag在创建vnode时也就是createVNode时,进行了赋值操作。如下
const shapeFlag = isString(type)
? ShapeFlags.ELEMENT
: __FEATURE_SUSPENSE__ && isSuspense(type)
? ShapeFlags.SUSPENSE
: isTeleport(type)
? ShapeFlags.TELEPORT
: isObject(type)
? ShapeFlags.STATEFUL_COMPONENT
: isFunction(type)
? ShapeFlags.FUNCTIONAL_COMPONENT
: 0
然后执行 createBaseVNode函数,其中如果有children,给shapeFlag重新赋值, 最终返回vnode
// 初始化为needFullChildrenNormalization为true
if (needFullChildrenNormalization) {
normalizeChildren(vnode, children)
// normalize suspense children
if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).normalize(vnode)
}
} else if (children) {
// TEXT_CHILDREN = 1 << 3, // 8
// ARRAY_CHILDREN = 1 << 4, // 16
vnode.shapeFlag |= isString(children)
? ShapeFlags.TEXT_CHILDREN
: ShapeFlags.ARRAY_CHILDREN
}
export function normalizeChildren(vnode: VNode, children: unknown) {
let type = 0
const { shapeFlag } = vnode
if (children == null) {
children = null
} else if (isArray(children)) {
type = ShapeFlags.ARRAY_CHILDREN // ARRAY_CHILDREN = 1 << 4, // 16
} else if (typeof children === 'object') {
if (shapeFlag & (ShapeFlags.ELEMENT | ShapeFlags.TELEPORT)) {
} else {
type = ShapeFlags.SLOTS_CHILDREN // SLOTS_CHILDREN = 1 << 5, // 32
}
} else if (isFunction(children)) {
children = { default: children, _ctx: currentRenderingInstance }
type = ShapeFlags.SLOTS_CHILDREN //SLOTS_CHILDREN = 1 << 5, // 32
} else {
children = String(children)
// force teleport children to array so it can be moved around
if (shapeFlag & ShapeFlags.TELEPORT) {
type = ShapeFlags.ARRAY_CHILDREN // ARRAY_CHILDREN = 1 << 4, // 16
children = [createTextVNode(children as string)]
} else {
type = ShapeFlags.TEXT_CHILDREN // TEXT_CHILDREN = 1 << 3, // 8
}
}
vnode.children = children as VNodeNormalizedChildren
vnode.shapeFlag |= type
}
这里的|=是或运算并赋值。 如果children是字符串则赋值9,否则赋值17。后边会根据此值进行与运算进行不同节点的处理。
大家也能明白这是什么意思,这里就讲讲ShapeFlags枚举,如下代码:大致明白就是定义属性并赋值。<< 这个双箭头代表左移然后低位补0,注意:它相对于二进制进行操作,例如 1==>0001
1<<1==>0010。
export const enum ShapeFlags {
ELEMENT = 1,
FUNCTIONAL_COMPONENT = 1 << 1, //2
STATEFUL_COMPONENT = 1 << 2, //4
TEXT_CHILDREN = 1 << 3, // 8
ARRAY_CHILDREN = 1 << 4, // 16
SLOTS_CHILDREN = 1 << 5, // 32
TELEPORT = 1 << 6, // 64
SUSPENSE = 1 << 7, //128
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, // 256
COMPONENT_KEPT_ALIVE = 1 << 9, // 512
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}
再回到patch函数中,如果我们在页面创建了const app = h('div',['12312',count.value]),这里的app就是vnode,其中children就是数组,即shapeFlag=17。const app = h('div',123);shapeFlag=9。
在利用shapeFlag&ShapeFlags.ELEMENT>0 也就是10001&0001=10001==>17,所以条件成立。与其他都为0,所以当前为元素节点。其他节点也是相同的计算,shapeFlag在上边也有讲,在创建vnode时会赋值。
default:
// 元素
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
// 组件
} else if (shapeFlag & ShapeFlags.COMPONENT) {
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
// 传送节点
} else if (shapeFlag & ShapeFlags.TELEPORT) {
;(type as typeof TeleportImpl).process(
n1 as TeleportVNode,
n2 as TeleportVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
// 异步类组件
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).process(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
} else if (__DEV__) {
warn('Invalid VNode type:', type, `(${typeof type})`)
}