1. 核心数据结构和工具函数
- 空
VNode
用于占位 patch
过程中的关键生命周期钩子- 判断两个
vnode
是否可以复用的核心逻辑
typescript
// 空的 VNode 节点
export const emptyNode = new VNode('', {}, [])
// patch 相关的生命周期钩子
const hooks = ['create', 'activate', 'update', 'remove', 'destroy']
// 判断两个 vnode 是否相同
function sameVnode(a, b) {
return (
a.key === b.key &&
a.asyncFactory === b.asyncFactory &&
((a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)) ||
(isTrue(a.isAsyncPlaceholder) && isUndef(b.asyncFactory.error)))
)
}
2. createPatchFunction 工厂函数
typescript
export function createPatchFunction(backend) {
// 初始化 modules 的钩子函数队列
const { modules, nodeOps } = backend
const cbs = {}
// 注册各个模块的钩子函数
for (i = 0; i < hooks.length; ++i) {
cbs[hooks[i]] = []
for (j = 0; j < modules.length; ++j) {
if (isDef(modules[j][hooks[i]])) {
cbs[hooks[i]].push(modules[j][hooks[i]])
}
}
}
// ...
}
这是一个工厂函数:
- 接收平台特定的节点操作方法
- 初始化各个模块的钩子函数队列
- 返回实际的
patch
函数
3. 核心 DOM 操作函数
typescript
function createElm(vnode, insertedVnodeQueue, parentElm, refElm) {
// 创建新节点
}
function removeNode(el) {
// 移除节点
}
function insert(parent, elm, ref) {
// 插入节点
}
这些是最基础的 DOM
操作封装:
createElm
: 创建新的DOM
节点removeNode
: 移除节点insert
: 插入节点
4. diff 算法核心 - updateChildren
- 采用双端比较算法提高效率
- 尽可能复用已有节点
- 处理节点的增删改
typescript
function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
// 双端比较算法
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
// 比较新旧子节点列表
// 四种命中查找
// 1. 新旧开始节点比较
// 2. 新旧结束节点比较
// 3. 旧开始与新结束比较
// 4. 旧结束与新开始比较
}
}
5. patch 的入口函数
typescript
return function patch(oldVnode, vnode, hydrating, removeOnly) {
// 1. vnode 不存在但是 oldVnode 存在,销毁旧节点
// 2. oldVnode 不存在,创建新节点
// 3. oldVnode 存在但不是真实节点且与 vnode 相同,执行 patchVnode
// 4. 其他情况,创建新节点替换旧节点
}
这是整个 patch
过程的入口:
- 处理各种新旧节点的组合情况
- 调用相应的处理函数
- 返回更新后的节点