const patch: PatchFn = (
n1, // 舊虛擬節點
n2, // 新虛擬節點
container,
anchor = null, // 定位錨點DOM,用於往錨點前插入節點
parentComponent = null,
parentSuspense = null,
isSVG = false,
slotScopeIds = null,
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren // 是否啟用 diff 優化
) => {
// 新舊虛擬節點相同,直接返回,不做 Diff 比較
if (n1 === n2) {
return
}
// patching & not same type, unmount old tree
// 新舊虛擬節點不相同(key 和 type 不同),則卸載舊的虛擬節點及其子節點
if (n1 && !isSameVNodeType(n1, n2)) {
anchor = getNextHostNode(n1)
// 卸載舊的虛擬節點及其子節點
unmount(n1, parentComponent, parentSuspense, true)
// 將 舊虛擬節點置為 null,保證後面走整個節點的 mount 邏輯
n1 = null
}
// PatchFlags.BAIL 標誌用於指示應該退出 diff 優化
if (n2.patchFlag === PatchFlags.BAIL) {
// optimized 置為 false ,在後續的 Diff 過程中不會啟用 diff 優化
optimized = false
// 將新虛擬節點的動態子節點數組置為 null,則不會進行 diff 優化
n2.dynamicChildren = null
}
const { type, ref, shapeFlag } = n2
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: // 處理 Fragment 元素
processFragment(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
// 處理 ELEMENT 類型的 DOM 元素
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) {
// 處理 Teleport 組件
// 調用 Teleport 組件內部的 process 函數,渲染 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) {
// 處理 Suspense 組件
// 調用 Suspense 組件內部的 process 函數,渲染 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})`)
}
}
// set ref
// 設置 ref 引用
if (ref != null && parentComponent) {
setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
}
}