02_前言
约 2999 字大约 10 分钟
2026-10-05
Vue.js 3.0 的诞生与设计
一次“集中还债”
Vue.js 作为最流行的前端框架之一,在 2020 年 9 月 18 日正式迎来了它的 3.0 版本。
技术债(technical debt):
为了赶进度或者受限于当时的技术水平,代码里留下的“以后再收拾”的妥协。这些妥协日积月累,会让后续改动越来越难。把这些旧问题解决掉,就叫“还技术债”。
得益于 Vue.js 2 的设计经验,Vue.js 3.0 不仅带来了诸多新特性,还在框架设计与实现上做了很多创新。在一定程度上,我们可以认为 Vue.js 3.0“还清”了在 Vue.js 2 中欠下的技术债务。
Vue.js 2 留下的某些设计在 3.0 里被全新重写,就是一次“集中还债”。
在我看来,Vue.js 3.0 是一个非常成功的项目。它秉承了 Vue.js 2 的易用性,同时相比 Vue.js 2,甚至做到了使用更少的代码来实现更多的功能。
模块化做得细
耦合度(coupling):
两个东西互相牵扯的程度。耦合度高 = 改一个,另一个也得跟着改;耦合度低 = 各干各的,改一个不会影响另一个。
低耦合的设计就像乐高积木,可以单独拆下来换别的零件。
运行时(runtime):
代码在浏览器里跑的时候才干活。对比之下,编译时是在打包阶段、还没在浏览器跑的时候就把工作干完了。
Vue.js 3.0 在模块的拆分和设计上做得非常合理。模块之间的耦合度非常低,很多模块可以独立安装使用,而不需要依赖完整的 Vue.js 运行时,例如 @vue/reactivity 模块。
Tree-Shaking(摇树):
打包工具(比如 webpack、rollup)的一项能力。想象你摇一棵苹果树,枯叶、没熟的果子会被摇掉,只留下成熟的苹果。
它就是把代码里“你没用到”的部分自动剔除掉,让最终文件更小。Vue.js 3 专门为它设计了模块结构。
Vue.js 3.0 在设计内建组件和模块时也花费了很多精力,配合构建工具以及 Tree-Shaking 机制,实现了内建能力的按需引入,从而实现了用户 bundle(构建工具打包出来的最终代码文件)的体积最小化。
扩展能力强
渲染器(renderer):
负责把“虚拟 DOM”翻译成“真实 DOM”(也就是你能在浏览器里看到的网页元素)。
Vue.js 3.0 的扩展能力非常强。我们可以编写自定义的渲染器,甚至可以编写编译器插件来自定义模板语法。同时,Vue.js 3.0 在用户体验上也下足了功夫。
Vue.js 3.0 的优点包括但不限于上述这些内容。既然优点如此之多,那么框架设计者是如何设计并实现这一切的呢?
实际上,理解 Vue.js 3.0 的核心设计思想非常重要。它不仅能让我们更加从容地面对复杂问题,还能指导我们在其他领域进行架构设计。
从规范出发
ECMAScript(简称 ES):
JavaScript 这门语言的官方标准文档。我们平时说的 ES6、ES2015、ES2022 都是它的不同版本。
WHATWG:
一个负责制定网页相关标准的组织。HTML 解析规范就出自他们之手。Vue.js 的模板解析器要“正确理解”一段 HTML 长什么样,就必须按 WHATWG 的规则来。
Proxy:
ES6 提供的一种“拦截”能力。你对一个对象设置“代理”之后,任何读取、修改、删除属性的操作都会先经过你设置的“中间人”,由你决定要不要放行、以及附加什么逻辑。
Vue.js 3 用它来实现“数据一变、界面自动变”的响应式系统。
Vue.js 3.0 中很多功能的设计需要谨遵规范。想要使用 Proxy 实现完善的响应系统,就必须从 ECMAScript 规范入手;Vue.js 的模板解析器则遵从 WHATWG 的相关规范。
所以在理解 Vue.js 3.0 核心设计思想的同时,我们还能够间接掌握阅读和理解规范,并据此编写代码的方法。
读者对象
本书的目标读者包括:
- 对 Vue.js 2/3 具有上手经验,且希望进一步理解 Vue.js 框架设计原理的开发人员。
- 没有使用过 Vue.js,但对 Vue.js 框架设计感兴趣的前端开发人员。
本书内容
本书内容并非“源码解读”,而是建立在笔者对 Vue.js 框架设计的理解之上,以由简入繁的方式介绍如何实现 Vue.js 中的各个功能模块。
本书将尽可能地从规范出发,实现功能完善且严谨的 Vue.js 功能模块。例如,通过阅读 ECMAScript 规范,基于 Proxy 实现一个完善的响应系统;通过阅读 WHATWG 规范,实现一个类 HTML 语法的模板解析器,并在此基础上实现一个支持插件架构的模板编译器。
除此之外,本书还会讨论以下内容。
- 框架设计的核心要素,以及框架设计过程中要做出的权衡
- 三种常见的虚拟 DOM(Virtual DOM)的 Diff 算法
- 组件化的实现,以及 Vue.js 内建组件的原理
- 服务端渲染、客户端渲染、同构渲染之间的差异,以及同构渲染的原理
上面提到的两个术语
Diff 算法(diff = difference,“差异”):
计算机里“找两个东西哪里不同”的算法。
Vue.js 用它来比较“页面现在的样子”和“页面应该变成的样子”,只去改那些不一样的地方。
同构渲染(isomorphic rendering / universal rendering):
同一份代码既能在服务器跑、也能在浏览器跑。
Vue.js 可以在服务器先把页面 HTML 生成出来给用户看,再在浏览器“激活”接管交互。
本书结构
本书分为 6 篇,共 18 章,各章的简介如下。
| 篇 | 章 | 主要内容 |
|---|---|---|
| 第一篇 框架设计概览 (3 章) | 第 1 章 | 命令式与声明式两种范式的差异及其对框架设计的影响;虚拟 DOM 的性能状况;运行时与编译时的相关知识;Vue.js 3.0 是一个运行时 + 编译时的框架 |
| 第 2 章 | 从开发体验、控制框架代码体积、Tree-Shaking 工作机制、框架产物、特性开关、错误处理、TypeScript 支持等几方面,讨论框架设计者应该考虑什么 | |
| 第 3 章 | 从全局视角介绍 Vue.js 3.0 的设计思路,以及各个模块之间如何协作 | |
| 第二篇 响应系统 (3 章) | 第 4 章 | 从宏观视角讲述响应系统的实现机制:从副作用函数出发逐步实现一个完善的响应系统;计算属性和 watch 的实现原理;实现过程中遇到的问题及解决方案 |
| 第 5 章 | 从 ECMAScript 规范入手,从最基本的 Proxy、Reflect 以及 JavaScript 对象的工作原理开始,逐步讨论如何用 Proxy 代理对象 | |
| 第 6 章 | ref 的概念;基于 ref 实现原始值的响应式方案;如何用 ref 解决响应丢失问题 | |
| 第三篇 渲染器 (5 章) | 第 7 章 | 渲染器与响应系统的关系,两者如何配合完成页面更新;渲染器中的基本名词和概念;自定义渲染器的实现与应用 |
| 第 8 章 | 挂载与更新的实现原理:子节点、属性、事件的处理;挂载或更新组件类型虚拟节点时的生命周期函数处理 | |
| 第 9 章 | “简单 Diff 算法”的工作原理 | |
| 第 10 章 | “双端 Diff 算法”的工作原理 | |
| 第 11 章 | “快速 Diff 算法”的工作原理 | |
| 第四篇 组件化 (3 章) | 第 12 章 | 组件实现原理:自身状态的初始化与自更新;外部状态(props)与被动更新;组件事件和插槽的实现原理 |
| 第 13 章 | 异步组件与函数式组件的工作机制和实现原理;异步组件的超时与错误处理、延迟展示 Loading 组件、加载重试 | |
| 第 14 章 | Vue.js 内建的三个组件的实现原理,即 KeepAlive、Teleport 和 Transition 组件 | |
| 第五篇 编译器 (3 章) | 第 15 章 | 模板编译器的工作流程;parser 的实现原理与状态机;AST 的转换与插件化架构;生成渲染函数代码的具体实现 |
| 第 16 章 | 实现符合 WHATWG HTML 解析规范的解析器:解析器的文本模式、文本模式对解析器的影响、递归下降算法构造模板 AST、根据规范解码字符引用 | |
| 第 17 章 | 编译优化:Block 树的更新机制、动态节点的收集、静态提升、预字符串化、缓存内联事件处理函数、v-once | |
| 第六篇 服务端渲染 (1 章) | 第 18 章 | 同构渲染原理:CSR、SSR 以及同构渲染各方案的优缺点;服务端渲染与客户端激活的原理;编写同构代码时的注意事项 |
源代码及勘误
在学习本书时,书中所有代码均可通过手敲进行试验和学习,你也可以从 GitHub(HcySunYang)下载所有源代码¹。
我将尽最大努力确保正文和源代码无误。但金无足赤,书中难免存在一些错误。如果读者发现任何错误,包括但不限于别字、代码片段、描述有误等,请及时反馈给我。本书勘误请至 GitHub(HcySunYang)查看或提交²。
¹ 或访问图灵社区本书主页下载。——编者注
² 也可访问图灵社区本书主页查看或提交勘误。——编者注
致谢
这本书的诞生,要感谢很多与之有直接或间接关系的人和物。下面的致谢不分先后,仅按照我特定的逻辑顺序组织编写。
首先要感谢 Vue.js 这个框架。毫无疑问,Vue.js 为世界创造了价值,无数的企业和个人开发者从中受益。当然,更要感谢 Vue.js 的作者尤雨溪以及 Vue.js 团队的其他所有成员,良好的团队运作使得 Vue.js 能够持续发展。
没有 Vue.js 就不可能有今天这本书。
除此之外,我还要感谢裕波老师的引荐和王军花老师的信任,使得我有机会来完成这样一本自己比较擅长的书。在写书的过程中,王军花老师全程热心细致的工作,让我对完成编写任务的信心得到了极大的提升。再次向裕波老师和王军花老师表示由衷的感谢,非常感谢。
还要感谢曾经共事过的一位朋友,张啸。他为这本书提出了很多宝贵意见,同时还细心地帮忙检查错别字和语句的表达问题。
感谢罗勇林,这是我另一位很特殊的朋友。可以说,没有他,我甚至不可能走上程序员的道路,更不可能写出这样一本书。谢谢你,兄弟。
当然了,我还要感谢刚刚和我成为合法夫妻的爱人。在我写书的时候,她还是我的女朋友,为了顺利地完成这本书和一些其他原因,我几乎在家全职写作,期间只有少量收入甚至没有收入。她从来没有抱怨过,还经常鼓励我,我还总开玩笑地对她说:“不然你养我算了。”
当这本书出版的时候,她已经成为我的合法妻子,这本书也是我送给她的礼物之一。谢谢你。
