⚡ 30 秒速记
- 核心算法与宿主节点操作解耦
- 宿主提供创建、插入、移除和属性更新能力
- 调度器决定何时执行,渲染器决定怎样应用
- 异步批处理不等于自动可中断
- 引用与生命周期要在正确完成边界触发
自定义渲染器把怎样操作平台这件事抽出来,让界面协调逻辑能复用。 浏览器实现用节点接口,其他宿主可以提供自己的创建、插入和属性操作,只要满足渲染器约定。调度是另一层问题,它决定什么时候刷新;把工作延后一次不代表长更新就能中途暂停。设计时还要保证引用、生命周期和清理看到的是一致的已完成状态。
版本校准: 本文出现 Object.defineProperty、Dep、Watcher 和双端 Diff 时,主要描述 Vue 2 实现;Vue 3 使用 Proxy、effect 与新的渲染器路径。Vue 2 已于 2023-12-31 结束维护,新项目应以 Vue 3 为基线,旧项目参考 Vue 2 EOL 官方说明 制定迁移与安全策略。
在本章之前,我们花费了很大的篇幅全面的讲解了一个普通渲染器的实现原理,它可以将
Virtual DOM渲染为 Web 平台的真实 DOM。本章我们将在上一章的基础上讲解更加高级的渲染器:自定义渲染器(Custom renderer)以及异步渲染。
# 自定义渲染器的原理
渲染器是围绕 Virtual DOM 而存在的,在 Web 平台下它能够把 Virtual DOM 渲染为浏览器中的真实 DOM 对象,通过前面几章的讲解,相信你已经能够认识到渲染器的实现原理,为了能够将 Virtual DOM 渲染为真实 DOM,渲染器内部需要调用浏览器提供的 DOM 编程接口,下面罗列了在出上一章中我们曾经使用到的那些浏览器为我们提供的 DOM 编程接口:
document.createElement / createElementNS:创建标签元素。document.createTextNode:创建文本元素。el.nodeValue:修改文本元素的内容。el.removeChild:移除 DOM 元素。el.insertBefore:插入 DOM 元素。el.appendChild:追加 DOM 元素。el.parentNode:获取父元素。el.nextSibling:获取下一个兄弟元素。document.querySelector:挂载Portal类型的VNode时,用它查找挂载点。
这些 DOM 编程接口完成了 Web 平台(或者说浏览器)下对 DOM 的增加、删除、查找的工作,它是 Web 平台独有的,所以如果渲染器自身强依赖于这些方法(函数),那么这个渲染器也只能够运行在浏览器中,它不具备跨平台的能力。换句话说,如果想要实现一个平台无关的渲染器,那么渲染器自身必须不能强依赖于任何一个平台下特有的接口,而是应该提供一个抽象层,将 “DOM” 的增加、删除、查找等操作使用抽象接口实现,具体到某个平台下时,由开发者决定如何使用该平台下的接口实现这个抽象层,这就是自定义渲染器的本质。