开篇词 解析 Vue.j 源码,提升编码能力

我现任 Zoom 前端架构师,曾先后于百度、滴滴从事前端研发工作。我平时喜欢钻研新技术、新框架,关注前端自动化、工程化、前端架构。和很多常年打磨自身编程能力的开发者一样,我对代码有洁癖,一直在努力追求高质量的代码。

为什么你要学习 Vue.js 源码?

前端技术日新月异的今天,前端应用的复杂度也在日益提升,熟练掌握一门 MVVM 前端开发框架已经成为必然要求,因为它能够很大程度上帮助前端开发者提高生产力。Vue.js、React 和Angular 是目前国内最流行的三个前端 MVVM 框架,其中 Vue.js 凭借轻量、易上手的优势收获了大批粉丝。

百度、阿里、腾讯、滴滴、头条、美团等大厂已经在大面积使用 Vue.js 开发 Web 前端项目,很多中小型公司也因为易上手,开发效率高而选用 Vue.js。此外,很多小程序的跨端方案,例如 uni-app、Mpx、chameleon、WePY 等框架也选择了类 Vue.js 的语法。总体而言,市场对于 Vue.js 人才的需求非常旺盛

但也正因为 Vue.js 上手门槛低,市场需求与人才现状间存在不少现实矛盾:

  • 很多初学者通过简单的培训后便入行,但所学大多是 Demo 级别的项目知识,到了真实的工作环境中往往水土不服;

  • 工作中只会简单地调用 API,而复杂的组件非常依赖开源的实现,如果找不到相关组件甚至难以完成开发需求;

  • 没有深入研究过,或者根本不懂 Vue.js 底层实现原理,开发中遇到 Bug 后不懂得如何分析解决问题,也不懂如何调试;

  • 工作中往往需要通过阅读源码去了解当前项目和一些第三方依赖库的实现方式和原理,但是简单的知识填充式的培训并不能教会这些,初学者也很难自己形成这样的能力。

初级开发人员已经很难满足当前市场需求,而高阶开发人员却显得供不应求。面试早已不只是考察你应用层面的掌握情况,面试官还喜欢考察技术背后的实现原理来判断你对技术的掌握程度,以及是否有对技术的钻研精神。如果你对于 Vue.js 的使用只是浮于表面,技术能力不过关,那你将很难在行业中立足。

以我多年的从业经历来看:

了解技术实现原理是前端工作的必然要求,而看源码是了解技术实现原理的最直接手法,是高效提升个人技术能力的有效途径。

此外,学习 Vue.js 源码还能够从更多层面提升你的技术实力:

首先,有助于提升你的 JavaScript 功底。Vue.js 源码底层是用纯原生 JavaScript 写的,你可以在阅读 Vue.js 源码的过程中学习很多 JavaScript 编程技巧。这种贴合实战的学习方式,比你天天抱着编程书看,效率要高得多。

其次,提升工作效率,形成学习与成长的良性循环。了解技术的底层实现原理,会让你在工作中对它的应用更加游刃有余,在遇到问题后可以快速定位并分析解决。这样你的工作效率就会大大提升,帮你省出更多的时间来学习和提升。

再次,借鉴优秀源码的经验,学习高手思路。你可以通过阅读优秀的项目源码,了解高手是如何组织代码的,了解一些算法思想和设计模式的应用,甚至培养“造轮子”的能力。实际上,Vue.js 3.0 的设计实现中就参考了很多优秀的开源 JavaScript 库。

最后,提升自己解读源码的能力。读源码本身是很好的学习方式,一旦你掌握了看源码的技巧,未来学习其他框架也会容易得多。而且,工作中也可以通过阅读项目已有代码快速熟悉项目,提高业务逻辑分析能力和重构代码的能力。

道理我都懂,就是做不到?

学习源码有这么多好处,很多人也明白这个道理,为什么却很少有人愿意去读源码呢?

  • 因为学习源码很枯燥,不像开发项目那样能够快速得到反馈、看到立竿见影的效果;

  • 学习源码相对于开发项目来说更抽象,理解起来也更难,很多人学着学着就放弃了;

  • 还有很多人想要更深入地学习 Vue.js,希望能够再进阶一个高度,却不得法门。

这正是我设计这个课程的原因之一。我希望结合自己多年研究源码和 Vue.js 实践经验,并结合一些在实际项目中的使用场景,来带你一起阅读源码,深入浅出地帮助你了解其技术实现原理。

我曾经使用 Vue.js 重构整个滴滴出行的 WebApp,负责其中的架构设计和组件库开发,也主导过 Vue.js 开源组件库 cube-ui 的开发。我也在 Zoom 工作期间为了配合安全组的 CSP 安全策略需求,通过直接魔改 Vue.js 源码的方式,开发了 Vue.js 2.x 的 CSP 兼容版本,该版本目前在 Zoom 内部运行稳定,服务于几十个用 Vue.js 做增强开发的页面。

此外,我平时喜欢写作和分享,曾经帮助很多人入门和进阶 Vue.js。在以往分享经验和对用户的答疑解惑过程中,我更加直观地感受到了 Vue.js 学习者的困惑之处,也懂得了如何才能帮助你更好地学习源码。

所以只要你能认真跟随我学习源码,你会发现原本枯燥的事情也许会变得有趣起来,随着你不断深入地理解 Vue.js 的实现,你也会越来越有成就感,学习的动力也就越来越强了。

课程设计

我会对 Vue.js 3.0 的源码进行透彻分析,但不会一味地去解释源码,而是更加注重解读 Vue.js 在实现某个 feature 的时候,它的设计思想是什么以及为什么会这么做。相比单纯解释源码这种“翻译”的工作,我更喜欢做“阅读理解”,把每部分源码的前因后果分析清楚。

课程共分三大模块,合计 22 篇文章。我会结合实际用例,循序渐进地带你深入 Vue.js 的内核实现。

  • 核心模块,我会带你分析 Vue.js 3.0 组件的实现原理、响应式原理,以及 Vue.js 3.0 新特性 Composition API 的实现原理。因为组件化一直都是 Vue.js 最核心的实现内容, Composition API  也是 Vue.js 3.0 非常亮眼的 API 设计,所以我会优先讲这两块内容。经过学习,你会对组件如何渲染和更新有深刻的理解,并且掌握 Composition API 背后的实现原理和应用场景。

  • 进阶模块,我会带你分析 Vue.js 3.0 模板的编译和优化过程。Vue.js 3.0 运行时的性能之所以有很大的提升,主要得益于其编译层面的优化,所以这部分内容是非常值得学习的,但由于它的难度较大,所以我把它设置成了进阶阶段。学完之后,你能够知道 Vue.js 是如何编译模板并生成代码的,以及编译过程背后的性能优化思想。

  • 扩展模块,前面你已经了解了 Vue.js 的核心实现和编译原理,那么接下来我会带你分析 Vue.js 3.0 的内置组件的实现原理Vue.js 3.0 一些实用特性的实现原理,以及 Vue.js 3.0 官方生态实现原理,这些内容非常贴合实际开发工作。学完之后,你会更加了解这些功能的实现原理和职责边界,在平时工作中应用起来更加得心应手。

当然,你的其他一些担忧,我也提前为你想到了:

Vue.js 源码是一直在更新维护的,课程中的一些代码片段可能会更新,但代码容易过时,思想并不会,所以相较于代码,我会更注重思想的解读,让你知其然也知其所以然;Vue.js 版本更新也会引入一些实用的新功能,届时我也会紧随其后对新功能做解读,并且更新我们这个线上课程,以便你能够学习到新的知识点;为了便于没有 TypeScript 经验的同学理解,我会尽量将编译后的 JavaScript 代码展示出来,并且通过注释说明代码的主要功能;我还会尽量精简代码的分支逻辑,方便你理解核心流程;结合图例帮助你理解一些晦涩难懂的代码功能;结合实际用例,让你可以更加直观地明白源码背后想要解决的实际场景问题。

总结

我在百度工作的时候需要写编译打包工具,于是期间我阅读了 FIS 和 Gulp 的源码;到了滴滴以后,我使用了 Vue.js 开发项目,就开始阅读 Vue.js 的源码;开源库 better-scroll,也是在我充分阅读 iScroll 源码的基础上重构并一点点优化出来的。通过不断学习源码,我逐渐搞懂了这些工具框架背后的设计思想,学习到很多优秀的编程技巧,大幅提升了我的学习效率和技术能力,让我受益匪浅。

因此,这门课我不仅希望帮你深入理解 Vue.js ,更希望带你提升读源码的能力,提升技术实力。

学习源码的过程就像在翻越一座座大山,但我会一直陪伴在你身边,做你坚强的后盾。学习的过程中,你可能会遇到一些问题,但是不要担心,你可以随时在评论区留言和提问,我会尽量抽出时间来认真解答你的提问。

准备好,让我们一起来感受 Vue.js 3.0 的美吧。

导读 一文看懂 Vue.j 3.0 的优化

我们的课程是要解读 Vue.js 框架的源码,所以在进入课程之前我们先来了解一下 Vue.js 框架演进的过程,也就是 Vue.js 3.0 主要做了哪些优化。

Vue.js 从 1.x 到 2.0 版本,最大的升级就是引入了虚拟 DOM 的概念,它为后续做服务端渲染以及跨端框架 Weex 提供了基础。

Vue.js 2.x 发展了很久,现在周边的生态设施都已经非常完善了,而且对于 Vue.js 用户而言,它几乎满足了我们日常开发的所有需求。你可能觉得 Vue.js 2.x 已经足够优秀,但是在 Vue.js 作者尤小右的眼中它还不够完美。在迭代 2.x 版本的过程中,小右发现了很多需要解决的痛点,比如源码自身的维护性,数据量大后带来的渲染和更新的性能问题,一些想舍弃但为了兼容一直保留的鸡肋 API 等;另外,小右还希望能给开发人员带来更好的编程体验,比如更好的 TypeScript 支持、更好的逻辑复用实践等,所以他希望能从源码、性能和语法 API 三个大的方面优化框架。

那么接下来,我们就一起来看一下 Vue.js 3.0 具体做了哪些优化。相信你学习完这篇文章,不仅能知道 Vue.js 3.0 的升级给我们开发带来的收益,还能学习到一些设计思想和理念,并在自己的开发工作中应用,获得提升。

源码优化

首先是源码优化,也就是小右对于 Vue.js 框架本身开发的优化,它的目的是让代码更易于开发和维护。源码的优化主要体现在使用 monorepo 和 TypeScript 管理和开发源码,这样做的目标是提升自身代码可维护性。接下来我们就来看一下这两个方面的具体变化。

1. 更好的代码管理方式:monorepo

首先,源码的优化体现在代码管理方式上。Vue.js 2.x 的源码托管在 src 目录,然后依据功能拆分出了 compiler(模板编译的相关代码)、core(与平台无关的通用运行时代码)、platforms(平台专有代码)、server(服务端渲染的相关代码)、sfc(.vue 单文件解析相关代码)、shared(共享工具代码) 等目录:

而到了 Vue.js 3.0 ,整个源码是通过 monorepo 的方式维护的,根据功能将不同的模块拆分到 packages 目录下面不同的子目录中:

可以看出相对于 Vue.js 2.x 的源码组织方式,monorepo 把这些模块拆分到不同的 package 中,每个 package 有各自的 API、类型定义和测试。这样使得模块拆分更细化,职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性。

另外一些 package(比如 reactivity 响应式库)是可以独立于 Vue.js 使用的,这样用户如果只想使用 Vue.js 3.0 的响应式能力,可以单独依赖这个响应式库而不用去依赖整个 Vue.js,减小了引用包的体积大小,而 Vue.js 2 .x 是做不到这一点的。

2. 有类型的 JavaScript:TypeScript

其次,源码的优化还体现在 Vue.js 3.0 自身采用了 TypeScript 开发。Vue.js 1.x 版本的源码是没有用类型语言的,小右用 JavaScript 开发了整个框架,但对于复杂的框架项目开发,使用类型语言非常有利于代码的维护,因为它可以在编码期间帮你做类型检查,避免一些因类型问题导致的错误;也可以利于它去定义接口的类型,利于 IDE 对变量类型的推导。

因此在重构 2.0 的时候,小右选型了 Flow,但是在 Vue.js 3.0 的时候抛弃 Flow 转而采用 TypeScript 重构了整个项目,这里有两方面原因,接下来我们具体说一下。

首先,Flow 是 Facebook 出品的 JavaScript 静态类型检查工具,它可以以非常小的成本对已有的 JavaScript 代码迁入,非常灵活,这也是 Vue.js 2.0 当初选型它时一方面的考量。但是 Flow 对于一些复杂场景类型的检查,支持得并不好。记得在看 Vue.js 2.x 源码的时候,在某行代码的注释中看到了对 Flow 的吐槽,比如在组件更新 props 的地方出现了:

const propOptions: any = vm.$options.props // wtf flow?

什么意思呢?其实是由于这里 Flow 并没有正确推导出 vm.$options.props 的类型 ,开发人员不得不强制申明 propsOptions 的类型为 any,显得很不合理;另外他也在社区平台吐槽过 Flow 团队的烂尾。

其次,Vue.js 3.0 抛弃 Flow 后,使用 TypeScript 重构了整个项目。 TypeScript提供了更好的类型检查,能支持复杂的类型推导;由于源码就使用 TypeScript 编写,也省去了单独维护 d.ts 文件的麻烦;就整个 TypeScript 的生态来看,TypeScript 团队也是越做越好,TypeScript 本身保持着一定频率的迭代和更新,支持的 feature 也越来越多。

此外,小右和 TypeScript 团队也一直保持了良好的沟通,我们可以期待 TypeScript 对 Vue.js 的支持会越来越好。

性能优化

性能优化一直是前端老生常谈的问题。那么对于 Vue.js 2.x 已经足够优秀的前端框架,它的性能优化可以从哪些方面进行突破呢?

1. 源码体积优化

首先是源码体积优化,我们在平时工作中也经常会尝试优化静态资源的体积,因为 JavaScript 包体积越小,意味着网络传输时间越短,JavaScript 引擎解析包的速度也越快。

那么,Vue.js 3.0 在源码体积的减少方面做了哪些工作呢?

  • 首先,移除一些冷门的 feature(比如 filter、inline-template 等);

  • 其次,引入 tree-shaking 的技术,减少打包体积。

第一点很好理解,所以这里我们来看看 tree-shaking,它的原理很简单,tree-shaking 依赖 ES2015 模块语法的静态结构(即 import 和 export),通过编译阶段的静态分析,找到没有引入的模块并打上标记。

举个例子,一个 math 模块定义了 2 个方法 square(x) 和 cube(x) :

export function square(x) {return x * x
}
export function cube(x) {return x * x * x
}

我们在这个模块外面只引入了 cube 方法:

import { cube } from './math.js'
// do something with cube

最终 math 模块会被 webpack 打包生成如下代码:

/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {'use strict';/* unused harmony export square *//* harmony export (immutable) */ __webpack_exports__['a'] = cube;function square(x) {return x * x;}function cube(x) {return x * x * x;}
});

可以看到,未被引入的 square 模块被标记了, 然后压缩阶段会利用例如 uglify-js、terser 等压缩工具真正地删除这些没有用到的代码。

也就是说,利用 tree-shaking 技术,如果你在项目中没有引入 Transition、KeepAlive 等组件,那么它们对应的代码就不会打包,这样也就间接达到了减少项目引入的 Vue.js 包体积的目的。

2. 数据劫持优化

其次是数据劫持优化。Vue.js 区别于 React 的一大特色是它的数据是响应式的,这个特性从 Vue.js 1.x 版本就一直伴随着,这也是 Vue.js 粉喜欢 Vue.js 的原因之一,DOM 是数据的一种映射,数据发生变化后可以自动更新 DOM,用户只需要专注于数据的修改,没有其余的心智负担。

在 Vue.js 内部,想实现这个功能是要付出一定代价的,那就是必须劫持数据的访问和更新。其实这点很好理解,当数据改变后,为了自动更新 DOM,那么就必须劫持数据的更新,也就是说当数据发生改变后能自动执行一些代码去更新 DOM,那么问题来了,Vue.js 怎么知道更新哪一片 DOM 呢?因为在渲染 DOM 的时候访问了数据,我们可以对它进行访问劫持,这样就在内部建立了依赖关系,也就知道数据对应的 DOM 是什么了。以上只是大体的思路,具体实现要比这更复杂,内部还依赖了一个 watcher 的数据结构做依赖管理,参考下图:

Vue.js 1.x 和 Vue.js 2.x 内部都是通过 Object.defineProperty 这个 API 去劫持数据的 getter 和 setter,具体是这样的:

Object.defineProperty(data, 'a',{get(){// track},set(){// trigger}
})

但这个 API 有一些缺陷,它必须预先知道要拦截的 key 是什么,所以它并不能检测对象属性的添加和删除。尽管 Vue.js 为了解决这个问题提供了 $set 和 $delete 实例方法,但是对于用户来说,还是增加了一定的心智负担。

另外 Object.defineProperty 的方式还有一个问题,举个例子,比如这个嵌套层级比较深的对象:

export default {data: {a: {b: {c: {d: 1}}}}
}

由于 Vue.js 无法判断你在运行时到底会访问到哪个属性,所以对于这样一个嵌套层级较深的对象,如果要劫持它内部深层次的对象变化,就需要递归遍历这个对象,执行 Object.defineProperty 把每一层对象数据都变成响应式的。毫无疑问,如果我们定义的响应式数据过于复杂,这就会有相当大的性能负担。

为了解决上述 2 个问题,Vue.js 3.0 使用了 Proxy API 做数据劫持,它的内部是这样的:

observed = new Proxy(data, {get() {// track},set() {// trigger}
})

由于它劫持的是整个对象,那么自然对于对象的属性的增加和删除都能检测到。

但要注意的是,Proxy API 并不能监听到内部深层次的对象变化,因此 Vue.js 3.0 的处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部对象才会变成响应式,而不是无脑递归,这样无疑也在很大程度上提升了性能,我会在后面分析响应式章节详细介绍它的具体实现原理。

3. 编译优化

最后是编译优化,为了便于理解,我们先来看一张图:

这是 Vue.js 2.x 从 new Vue 开始渲染成 DOM 的流程,上面说过的响应式过程就发生在图中的 init 阶段,另外 template compile to render function 的流程是可以借助 vue-loader 在 webpack 编译阶段离线完成,并非一定要在运行时完成。

所以想优化整个 Vue.js 的运行时,除了数据劫持部分的优化,我们可以在耗时相对较多的 patch 阶段想办法,Vue.js 3.0 也是这么做的,并且它通过在编译阶段优化编译的结果,来实现运行时 patch 过程的优化。

我们知道,通过数据劫持和依赖收集,Vue.js 2.x 的数据更新并触发重新渲染的粒度是组件级的:

虽然 Vue 能保证触发更新的组件最小化,但在单个组件内部依然需要遍历该组件的整个 vnode 树,举个例子,比如我们要更新这个组件:

<template><div id="content"><p class="text">static text</p><p class="text">static text</p><p class="text">{{message}}</p><p class="text">static text</p><p class="text">static text</p></div>
</template>

整个 diff 过程如图所示:

可以看到,因为这段代码中只有一个动态节点,所以这里有很多 diff 和遍历其实都是不需要的,这就会导致 vnode 的性能跟模版大小正相关,跟动态节点的数量无关,当一些组件的整个模版内只有少量动态节点时,这些遍历都是性能的浪费。

而对于上述例子,理想状态只需要 diff 这个绑定 message 动态节点的 p 标签即可。

Vue.js 3.0 做到了,它通过编译阶段对静态模板的分析,编译生成了 Block tree。Block tree 是一个将模版基于动态节点指令切割的嵌套区块,每个区块内部的节点结构是固定的,而且每个区块只需要以一个 Array 来追踪自身包含的动态节点。借助 Block tree,Vue.js 将 vnode 更新性能由与模版整体大小相关提升为与动态内容的数量相关,这是一个非常大的性能突破,我会在后续的章节详细分析它是如何实现的。

除此之外,Vue.js 3.0 在编译阶段还包含了对 Slot 的编译优化、事件侦听函数的缓存优化,并且在运行时重写了 diff 算法,这些性能优化的内容我在后续特定的章节与你分享。

语法 API 优化:Composition API

除了源码和性能方面,Vue.js 3.0 还在语法方面进行了优化,主要是提供了 Composition API,那么我们一起来看一下它为我们提供了什么帮助。

1. 优化逻辑组织

首先,是优化逻辑组织。

在 Vue.js 1.x 和 2.x 版本中,编写组件本质就是在编写一个“包含了描述组件选项的对象”,我们把它称为 Options API,它的好处是在于写法非常符合直觉思维,对于新手来说这样很容易理解,这也是很多人喜欢 Vue.js 的原因之一。

Options API 的设计是按照 methods、computed、data、props 这些不同的选项分类,当组件小的时候,这种分类方式一目了然;但是在大型组件中,一个组件可能有多个逻辑关注点,当使用 Options API 的时候,每一个关注点都有自己的 Options,如果需要修改一个逻辑点关注点,就需要在单个文件中不断上下切换和寻找。

举一个官方例子 Vue CLI UI file explorer,它是 vue-cli GUI 应用程序中的一个复杂的文件浏览器组件。这个组件需要处理许多不同的逻辑关注点:

  • 跟踪当前文件夹状态并显示其内容

  • 处理文件夹导航(比如打开、关闭、刷新等)

  • 处理新文件夹的创建

  • 切换显示收藏夹

  • 切换显示隐藏文件夹

  • 处理当前工作目录的更改

如果我们按照逻辑关注点做颜色编码,就可以看到当使用 Options API 去编写组件时,这些逻辑关注点是非常分散的:

Vue.js 3.0 提供了一种新的 API:Composition API,它有一个很好的机制去解决这样的问题,就是将某个逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去。

通过下图,我们可以很直观地感受到 Composition API 在逻辑组织方面的优势:

2. 优化逻辑复用

其次,是优化逻辑复用。

当我们开发项目变得复杂的时候,免不了需要抽象出一些复用的逻辑。在 Vue.js 2.x 中,我们通常会用 mixins 去复用逻辑,举一个鼠标位置侦听的例子,我们会编写如下函数 mousePositionMixin:

const mousePositionMixin = {data() {return {x: 0,y: 0}},mounted() {window.addEventListener('mousemove', this.update)},destroyed() {window.removeEventListener('mousemove', this.update)},methods: {update(e) {this.x = e.pageXthis.y = e.pageY}}
}
export default mousePositionMixin

然后在组件中使用:

<template><div>Mouse position: x {{ x }} / y {{ y }}</div>
</template>
<script>
import mousePositionMixin from './mouse'
export default {mixins: [mousePositionMixin]
}
</script>

使用单个 mixin 似乎问题不大,但是当我们一个组件混入大量不同的 mixins 的时候,会存在两个非常明显的问题:命名冲突和数据来源不清晰。

首先每个 mixin 都可以定义自己的 props、data,它们之间是无感的,所以很容易定义相同的变量,导致命名冲突。另外对组件而言,如果模板中使用不在当前组件中定义的变量,那么就会不太容易知道这些变量在哪里定义的,这就是数据来源不清晰。但是Vue.js 3.0 设计的 Composition API,就很好地帮助我们解决了 mixins 的这两个问题。

我们来看一下在 Vue.js 3.0 中如何书写这个示例:

import { ref, onMounted, onUnmounted } from 'vue'
export default function useMousePosition() {const x = ref(0)const y = ref(0)const update = e => {x.value = e.pageXy.value = e.pageY}onMounted(() => {window.addEventListener('mousemove', update)})onUnmounted(() => {window.removeEventListener('mousemove', update)})return { x, y }
}

这里我们约定 useMousePosition 这个函数为 hook 函数,然后在组件中使用:

<template><div>Mouse position: x {{ x }} / y {{ y }}</div>
</template>
<script>import useMousePosition from './mouse'export default {setup() {const { x, y } = useMousePosition()return { x, y }}}
</script>

可以看到,整个数据来源清晰了,即使去编写更多的 hook 函数,也不会出现命名冲突的问题。

Composition API 除了在逻辑复用方面有优势,也会有更好的类型支持,因为它们都是一些函数,在调用函数时,自然所有的类型就被推导出来了,不像 Options API 所有的东西使用 this。另外,Composition API 对 tree-shaking 友好,代码也更容易压缩。

虽然 Composition API 有诸多优势,它也不是一点缺点都没有,关于它的具体用法和设计原理,我们会在后续的章节详细说明。这里还需要说明的是,Composition API 属于 API 的增强,它并不是 Vue.js 3.0 组件开发的范式,如果你的组件足够简单,你还是可以使用 Options API。

引入 RFC:使每个版本改动可控

作为一个流行开源框架的作者,小右可能每天都会收到各种各样的 feature request。但并不是社区一有新功能的需求,框架就会立马支持,因为随着 Vue.js 的用户越来越多,小右会更加重视稳定性,会仔细考虑所做的每一个可能对最终用户影响的更改,以及有意识去防止新 API 对框架本身实现带来的复杂性的提升。

因此在 Vue.js 2.x 版本开发到后期的阶段 ,小右就启用了 RFC ,它的全称是 Request For Comments,旨在为新功能进入框架提供一个一致且受控的路径。当社区有一些新需求的想法时,它可以提交一个 RFC,然后由社区和 Vue.js 的核心团队一起讨论,如果这个 RFC 最终被通过了,那么它才会被实现。比如 2.6 版本对于 slot 新 API 的改动,就是这条 RFC 里。

到了 Vue.js 3.0 ,小右在实现代码前就大规模启用 RFC,来确保他的改动和设计都是经过讨论并确认的,这样可以避免走弯路。Vue.js 3.0 版本有很多重大的改动,每一条改动都会有对应的 RFC,通过阅读这些 RFC,你可以了解每一个 feature 采用或被废弃掉的前因后果。

Vue.js 3.0 目前已被实现并合并的 RFC 都在这里,通过阅读它们,你也可以大致了解 Vue.js 3.0 的一些变化,以及为什么会产生这些变化,帮助你了解它的前因后果。

过渡期

接下来,我想再带你来了解一下 Vue.js 各版本迭代的过渡期,希望能够对你在 Vue.js 的技术选型方面和学习方向上有所帮助。

通常框架的 major 版本从升级到大规模投入使用,都需要经历相当长的一段过渡期。不过, Vue.js 1.x 到 Vue.js 2.0 的升级过渡期不长,主要是因为那个时候 Vue.js 的用户还不多,生态也不完善,很多用户都是直接上手的 2.0 版本,没有旧项目的历史包袱。

而 Vue.js 2.x 的发展历经了 3 年多的时间,用户众多,而且周边生态也已经非常完善了。通常 major 版本的升级会有很多 breaking change,这就意味着想从 2.x 升级到 3.0 的项目需要改代码,而且不仅仅项目的代码要修改,所依赖的周边生态也需要升级。这其实是一个相当大的工作量,也需要承担一定的风险,所以如果你的项目非常庞大且已经相对稳定,没有什么特别的痛点,那么升级要慎重。

Vue.js 3.0 使用 ES2015 的语法开发,有些 API 如 Proxy 是没有 polyfill 的,这就意味着官方需要单独出一个 IE11 compat 版本来支持 IE11。如果你的项目需要兼容 IE11,你就不得不小心使用某些 API,这也就带来了一些额外的心智负担。

因此可能在 Vue.js 3.0 出来的相当长的一段时间,复杂的大项目都不会考虑去升级,而一些小的、对浏览器兼容要求不高的新项目可以考虑尝鲜了。

官方会继续维护 Vue.js 2.x 版本 18 个月,如果你的有些项目一辈子都不打算升级 Vue.js 3.0,那么你应该去认真学习 Vue.js 2.x 的源码,在官方不再维护的时候遇到问题你可以自己去修改它的源码来解决。

不过,虽然 Vue.js 3.0 距离大规模应用还有相当长一段时间,但是越早开始学习你就越能在未来掌握主动权。这段时间里,你可以关注它的发展,去学习它的设计思想,也可以去为它的生态建设贡献代码,从而提升自己的技术能力。另外也可以尝试在一些小项目中应用 Vue.js 3.0,不仅可以享受 Vue.js 3.0 带来的性能方面的优势以及 Composition API 在逻辑复用方面便利,也为了将来某一天全面升级 Vue.js 3.0 做技术储备。

总结

这节课我们主要讲解了 Vue.js 3.0 升级做了几个方面的优化,以及为什么会需要这些优化。希望学习完后我们也可以像小右一样去审视自己的工作,有哪些痛点,找到可以改进和努力的方向并实施,只有这样你才能够不断提升自己的能力,工作上也会有不错的产出。

Vue.js 3.0 做了这么多改进,相信你也一定对它的实现细节非常感兴趣,那么在接下来的课程里,就让我对 Vue.js 的源码抽丝剥茧,一层层为你揭开 Vue.js 背后的实现原理和细节。那么还等什么,快上车吧!

Vue3核心源码解析第一课 看懂Vue3的优化相关推荐

  1. Vue3核心源码解析第十一课 AST生成代码

    16 生成代码:AST 如何生成可运行的代码?(上) 上一节课我们分析了 AST 节点转换的过程,也知道了 AST 节点转换的作用是通过语法分析,创建了语义和信息更加丰富的代码生成节点 codegen ...

  2. 面试官系统精讲Java源码及大厂真题 - 09 TreeMap 和 LinkedHashMap 核心源码解析

    09 TreeMap 和 LinkedHashMap 核心源码解析 更新时间:2019-09-05 10:15:03 人的影响短暂而微弱,书的影响则广泛而深远. --普希金 引导语 在熟悉 HashM ...

  3. 新书上市 | Vue 3.0 核心源码解析,这本书给Vue学习提供新方法

    Vue.js 作为一款极简的 MVVM 框架,因其轻量.易上手,得到了众多开发者的喜爱. 自从 2014 年 Vue 诞生以来,这个框架设计的初衷,尤大说只是为了设计一个让自己用起来舒服的框架,随着受 ...

  4. Netty 核心源码解析

    Netty 第一讲:Netty 架构与原理 本文是<Netty 三讲>的第二讲:Netty 核心源码解析(部分),大纲如下: 前言 1. Netty 服务端启动过程源码剖析 1.1. 执行 ...

  5. RocketMQ源码系列(一) NameServer 核心源码解析

    目录 一.NameServer 介绍 二.NameServer 功能列表 三.NameServer 架构分析 四.NameServer 工程目录解析 五.NameServer 启动流程分析 1)  创 ...

  6. Sentinel核心源码解析

    Sentinel核心源码解析 Sentinel是分布式系统的防御系统.以流量为切入点,通过动态设置的流量控制.服务熔断等手段达到保护系统的目的,通过服务降级增强服务被拒后用户的体验. 一.Sentin ...

  7. Android Volley核心源码解析

    君不见,黄河之水天上来,奔流到海不复回. 君不见,高堂明镜悲白发,朝如青丝暮成雪! 人生得意须尽欢,莫使金樽空对月. 天生我材必有用,千金散尽还复来. 烹羊宰牛且为乐,会须一饮三百杯. 岑夫子,丹丘生 ...

  8. Kafka核心源码解析 - KafkaController源码解析

    在进入源码解析之前,我先来介绍一些KafkaController在Kafka集群中的作用. (1)负责监听zookeeper上所有的元数据变更请求: (2)负责topic的partition迁移任务分 ...

  9. Python requests库核心源码解析

    Requests is an elegant and simple HTTP library for Python, built for human beings. Python requests是最 ...

最新文章

  1. Java虚拟机的内存空间有几种
  2. ART基因序列生成器,究竟是做什么的?
  3. Pivotal Tuning for Latent-based Editing of Real Images
  4. webpack 实践笔记(一)--- 入门
  5. 论文研读:Automatic Temporal Segment Detection and Affect Recognition From Face and Body Display
  6. iOS开发两个距离较近的按钮同时触发事件的解决方法
  7. linux系统结构与文件管理命令
  8. C# 连接 Oracle 的几种方式
  9. Android之网络调试adb tcpip
  10. java中的POJO、PO、VO分别是什么?
  11. SparkSql与Redis综合练习
  12. Javascript当中的RSA加解密
  13. Shiro - RememberMe记住我功能实现
  14. 51单片机,485,测试03/05/06
  15. C:\Windows\System32\drivers\etc\hosts文件的作用说明
  16. 常用计算机防治病毒软件有哪些,如何防治计算机病毒?常用的反病毒软件有哪些?...
  17. 毕业设计之基于Qt数字音频处理软件设计
  18. flash火焰燃烧的文字效果
  19. “大数据”挖出老鼠仓的政治价值
  20. “双一流”,中国矿业大学(北京)和华为签约

热门文章

  1. Linux设置字符界面的字体大小和字体类型
  2. ireport报表分页导致多一张空白页
  3. openfire android 发送图片,基于openfire+smack开发Android即时聊天应用[四]-单人聊天、群聊、发送接收文件等...
  4. Kettle(2):安装Kettle
  5. 音乐咖android,有音乐的料理店,即将开档!
  6. error A2031: gisters not allowed【汇编】
  7. 【ps教程】如何给照片换背景
  8. Vue3通透教程【五】Vue3中的响应式数据 reactive函数、ref函数
  9. iphone mac 隔空投送
  10. VC应用程序的界面换肤工具集合