patch 算法:将虚拟 DOM 变成真实 DOM

1. 初始化 patch(container, vnode)

将 vnode 创建为真实 DOM,并插入到容器中。

2. 更新 update(vnode, newVnode)

对比新旧 vnode 的差异,以最小代价更新真实 DOM。

function createElement(vnode) {
  let tag = vnode.tag        // 目标元素
  let attrs = vnode.attrs || {}
  let children = vnode.children || []

  if (!tag) return null

  let elem = document.createElement(tag)
  let attrName

  for (attrName in attrs) {
    if (attrs.hasOwnProperty(attrName)) {
      elem.setAttribute(attrName, attrs[attrName])
    }
  }

  children.forEach(childVnode => {
    elem.appendChild(createElement(childVnode))
  })
  return elem
}

function updateChildren(vnode, newVnode) {
  let children = vnode.children || []
  let newChildren = newVnode.children || []
  children.forEach((childrenVnode, index) => {
    let newChildrenVnode = newChildren[index]
    if (childrenVnode.tag === newChildrenVnode.tag) {
      updateChildren(childrenVnode, newChildrenVnode)
    } else {
      replaceNode(childrenVnode, newChildrenVnode)
    }
  })
}

补充:上述是 patch / diff 的极简实现。真正的 Vue 源码里会处理:

  • 节点 key:通过 key 在 map 中查找可复用节点;
  • 双端指针 oldStart / oldEnd / newStart / newEnd 四向比较;
  • 节点的增删改:在合适的位置插入 / 移动 / 删除真实 DOM;
  • 组件级别的 patch:递归处理组件实例、props 变化、生命周期触发等。

来源整理自:vue3js.cn 面试官系列