⚡ 30 秒速记

  • 可复用先对身份与类型,不兼容就替换
  • 复用宿主节点后更新属性与孩子
  • 旧属性消失必须移除
  • 文本、空孩子、节点列表之间要覆盖转换
  • 事件更新避免旧监听残留与重复注册

patch 的任务是让已有界面与新描述一致,同时复用仍然合适的节点。 身份或类型不兼容就替换,能复用时保留宿主节点,再比较属性和孩子。更新不只是写新值,旧样式、属性和事件消失后也要撤销。孩子从文本变列表、从列表变空这些边界尤其容易遗漏,要逐种定义如何卸载和挂载,才能避免页面残留。

版本校准: 本文出现 Object.defineProperty、Dep、Watcher 和双端 Diff 时,主要描述 Vue 2 实现;Vue 3 使用 Proxy、effect 与新的渲染器路径。Vue 2 已于 2023-12-31 结束维护,新项目应以 Vue 3 为基线,旧项目参考 Vue 2 EOL 官方说明 制定迁移与安全策略。

在上一章中我们讲解并实现了渲染器的挂载逻辑,本质上就是将各种类型的 VNode 渲染成真实DOM的过程。渲染器除了将全新的 VNode 挂载成真实DOM之外,它的另外一个职责是负责对新旧 VNode 进行比对,并以合适的方式更新DOM,也就是我们常说的 patch。本章内容除了让你了解基本的比对逻辑之外,还讲述了在新旧 VNode 比对的过程中应该遵守怎样的原则,让我们开始吧!

# 基本原则

通常重渲染(re-render)是由组件的更新开始的,因为在框架的使用层面开发者通过变更数据状态从而引起框架内部对UI的自动更新,但是组件的更新本质上还是对真实DOM的更新,或者说是对标签元素的更新,所以我们就优先来看一下如何更新一个标签元素。

我们首先回顾一下渲染器的代码,如下:

function render(vnode, container) {
  const prevVNode = container.vnode
  if (prevVNode == null) {
    if (vnode) {
      // 没有旧的 VNode,使用 `mount` 函数挂载全新的 VNode
      mount(vnode, container)
      // 将新的 VNode 添加到 container.vnode 属性下,这样下一次渲染时旧的 VNode 就存在了
      container.vnode = vnode
    }
  } else {
    if (vnode) {
      // 有旧的 VNode,则调用 `patch` 函数打补丁
      patch(prevVNode, vnode, container)
      // 更新 container.vnode
      container.vnode = vnode
    } else {
      // 有旧的 VNode 但是没有新的 VNode,这说明应该移除 DOM,在浏览器中可以使用 removeChild 函数。
      container.removeChild(prevVNode.el)
      container.vnode = null
    }
  }
}
webapp
公众号
开发者导航
切换夜间模式
点击侧边栏上一篇
点击侧边栏下一篇
折叠侧边栏
收起全部