⚡ 30 秒速记
- 核心判断:虚拟节点是对界面结构的描述,渲染器通过挂载与更新把声明式描述映射为宿主环境操作
- 原理主线:围绕 「在VNode创建时确定其类型 - fla」、「在VNode创建时确定其children」、「使用 h 函数创建 VNode」 建立输入、状态变化与输出之间的因果关系
- 文章范围:介绍了Vue中用于辅助创建VNode的h函数的原理、用法及其在虚拟DOM渲染流程中的作用,帮助开发者理解h函数如何提升开发效率及其与模板、JSX的关系。
- 边界与代价:
key、节点类型和稳定序列决定复用边界;虚拟 DOM 解决可预测更新,不保证永远比手写 DOM 快 - 工程落地:性能分析要看实际提交次数、节点移动和组件边界,不能只用“减少 DOM 操作”解释
h 函数是创建 VNode 的运行时封装,它根据 tag、data 和 children 生成带有明确类型信息的虚拟节点。 它会从 tag 判断普通元素、组件、Fragment 或 Portal,再根据 children 区分无子节点、单节点和多节点,并把普通文本转换成文本 VNode。多个子节点还会经过规范化处理,为缺少 key 的节点补充标识。需要注意,组件的子内容应转成 slots,不能完全套用普通元素的子节点处理方式。
这篇文章不要按 API 清单来背。先用上面的 Mind Map 建立全局结构,再通过交互 DEMO 观察正常路径和边界路径如何改变状态;阅读正文时重点核对每一步的输入、负责执行的参与者、产生的中间状态以及最终可观察结果。遇到版本敏感结论,要把“历史实现”“当前行为”和“工程兼容策略”分开说明;遇到性能或架构取舍,则用实际指标、失败现象和验证手段支撑判断。
版本校准: 本文出现 Object.defineProperty、Dep、Watcher 和双端 Diff 时,主要描述 Vue 2 实现;Vue 3 使用 Proxy、effect 与新的渲染器路径。Vue 2 已于 2023-12-31 结束维护,新项目应以 Vue 3 为基线,旧项目参考 Vue 2 EOL 官方说明 制定迁移与安全策略。
自从有了
VNode,开发页面的方式就变成了书写VNode,但如果日常开发中需要手写VNode,那绝对是反人类的,在“组件的本质”一章中我们使用了snabbdom的h函数来辅助讲解一些小例子,h函数作为创建VNode对象的函数封装,在一定程度上改善了这个问题,但却没有解决本质问题,这也是为什么我们需要模板或jsx的原因。但h函数依然很重要,因为无论是模板还是jsx都需要经过编译,那么是直接编译成VNode树好呢?还是编译成由h函数组成的调用集合好呢?这个其实很难说,但可以肯定的一点是,我们将可公用、灵活、复杂的逻辑封装成函数,并交给运行时,这能够大大降低编译器的书写难度,甚至经过编译器生成的代码也具有一定的可读性,而h函数就是众多运行时函数中的一个。