Skip to content

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 过程的入口:

  • 处理各种新旧节点的组合情况
  • 调用相应的处理函数
  • 返回更新后的节点