观感度:🌟🌟🌟🌟🌟前端
口味:麻辣小龙虾vue
烹饪时间:10mingit
本文已收录在Github github.com/Geekhyt,感谢Star。原文地址:https://increment.com/frontend/making-vue-3/github
译者:童欧巴算法
因时间有限,文中翻译不对的地方还请指出,望海涵。想感觉原汁原味还请移步上方连接。致敬尤大!后端
EVAN YOU
数组
重写 Vue.js 下一个主要版本的经验
浏览器
在过去的一年中,Vue 团队一直在研究 Vue.js 的下一个主要版本,咱们但愿在 2020 年上半年发布该版本。(在撰写本文时,这项工做仍在进行中。)Vue 的新的主要版本的构想造成于 2018 年年底,当时 Vue 2 的代码库已经两年半大了。在通用软件的生命周期中听起来可能并不长,可是在此期间,前端环境发生了翻天覆地的变化。前端框架
有两个主要的因素促使咱们开发(重写) Vue 新的主要版本:首先是主流浏览器广泛支持了新的 JavaScript 语言特性。其次,随着时间的推移,当前代码库中的设计和架构问题已经暴露出来。架构
随着 ES2015 的标准化,JavaScript(正式称为 ECMAScript ,缩写为 ES)获得了重大改进,主流浏览器终于开始为这些新特性提供良好的支持。特别是其中一些新特性为咱们极大提升 Vue 的性能提供了机会。
其中最值得关注的是 Proxy,它容许框架拦截对象上的操做。Vue 的核心功能是可以侦听用户定义的状态变动并响应式的更新 DOM。Vue2 是经过使用 getter 和 setter 替换响应式对象上的属性来实现的这个功能。换做使用 Proxy 可以消除 Vue 现存的一些限制,例如没法检测到新的属性添加并为其提供更好的性能。
然而,Proxy 是语言原生的特性,在旧版本的浏览器中不能被彻底的支持。为了使用它,咱们意识到必须调整框架的浏览器支持范围,这是一个突破性的变化 (breaking change),也只能在新的主要版本中发布。
在当前代码库汇总解决这些问题,重构的风险较大,几乎等同于重写
在维护 Vue 2 的过程当中,因为现有架构的限制,咱们积累了许多难以解决的问题。例如,模板编译器的编写方式使适当的 source-map 支持很是具备挑战性。一样,虽然 Vue 2 从技术上容许构建针对非 DOM 平台的更高级别的渲染器,但咱们必须 fork 代码库并复制大量代码,才能实现这一点。要在当前代码库中解决这些问题,重构的风险很大,几乎等同于重写。
同时,咱们在各类模块的内部与浮动代码之间的隐式耦合上积累了技术债,而浮动代码 (floating code) 彷佛并不属于任何地方。这使得单独理解代码库的一部分变得更加困难,而且咱们注意到,贡献者对重要的更改很没有信心,重写将使咱们有机会牢记这些注意事项来从新考虑代码组织。
咱们于 2018 年末开始对 Vue 3 进行原型设计,其初步目标是验证这些问题的解决方案的可行性。在此阶段,咱们主要致力于为进一步发展奠基坚实的基础。
Vue 2 最初是用纯 ES 编写的。在进入原型开发阶段以后不久,咱们意识到类型系统对于这种规模的项目将会很是有帮助。类型检查极大地减小了在重构期间引入意外错误的机会,并有助于贡献者更自信地进行重要的更改。咱们采用了 Facebook 的 Flow 类型检查器,由于它能够逐渐添加到现有的 Plain-ES 项目中。 Flow 在必定程度上有所帮助,可是咱们没有从中得到咱们但愿的最大收益。特别是不断的变化使升级变得很痛苦。与 TypeScript 和 Visual Studio Code 之间的深度集成相比,对集成开发环境的支持也不理想。
咱们还注意到,用户愈来愈多地同时使用 Vue 和 TypeScript。为了支持它们的用例,咱们在不一样的类型系统下,必须与源代码分开编写和维护 TypeScript 声明。切换到 TypeScript 将使咱们可以自动生成声明文件,从而减轻了维护负担。
咱们还采用了 monorepo 设置框架的内部 packages 构成,每一个 package 都具备本身的独立 API,类型定义和测试用例。咱们但愿使这些模块之间的依赖关系更加明确,从而使开发人员更容易阅读、理解并进行全部更改。这是咱们努力下降该项目的贡献障碍并提升其长期可维护性的关键。
到 2018 年末,咱们有了一个使用新的响应式系统和虚拟 DOM 渲染器的工做原型。咱们已经验证了咱们想要进行的内部体系结构改进,可是只包含了面向公众的 API 更改的草稿 (drafts)。是时候将它们变成具体的设计了。
咱们知道咱们必须未雨绸缪。 Vue 的普遍使用意味着突破性变化 (breaking changes) 可能致使用户大量迁移成本和潜在的生态系统碎片化。为了确保用户可以提供有关重大更改的反馈,咱们于 2019 年初采用了 RFC(征求意见)流程。每一个 RFC 遵循一个模板,各章节重点介绍动机、设计细节以及取舍和权衡策略。因为此过程是在 GitHub 仓库中进行的,提案经过 pull requests 形式,所以讨论在评论中 (comments) 天然展开。
事实证实,RFC 流程很是有用,它是一个使咱们可以充分考虑潜在变动的全方位的思想框架,而且容许咱们的社区参与设计过程并提交通过深思熟虑的feature requests。
性能对于前端框架相当重要
性能对于前端框架相当重要。尽管 Vue 2 具备出色的性能,但经过尝试新的渲染策略重写,提供了进一步提高性能的机会。
Vue有一个至关独特的渲染策略:它提供了相似于 HTML 的模板语法,可是将模板编译为可返回虚拟 DOM 树的渲染函数 (render function)。该框架经过递归遍历两个虚拟 DOM 树并比较每一个节点上的每一个属性来肯定真实 DOM 的哪些部分须要更新。得益于现代 JavaScript 引擎执行的高级优化,所以这种有点蛮力的算法一般很快,可是更新仍然涉及许多没必要要的 CPU 工做。当您查看的模板中大部分是静态内容而且只有少许动态绑定时,效率低下尤为明显-整个虚拟 DOM 树仍须要递归遍历以找出更改之处。
幸运的是,模板编译步骤使咱们有机会对模板进行静态分析并提取有关动态部分的信息。Vue 2 经过跳过静态子树在某种程度上作到了这一点,不过因为编译器体系结构过于简单,难以进行更高级的优化。在 Vue 3中,咱们使用适当的 AST 转换 pipeline 重写了编译器,这使咱们可以以转换插件的形式编写编译时的优化。
有了新的体系结构,咱们但愿找到一种尽量减小开销的渲染策略。一种选择是放弃虚拟 DOM 并直接生成命令式 DOM 操做,但这将消除直接编写虚拟 DOM 渲染功能的能力 (render functions),咱们发现该功能对于高级用户和库的做者很是有价值。另外,这将是一个突破性的变化 (breaking change)。
其次,最佳的方案是消除没必要要的虚拟 DOM 树遍历和属性比较,这在更新过程当中每每会带来最大的性能开销。为了实现这一点,编译器 (compiler) 和运行时 (runtime) 须要协同工做:编译器分析模板并生成带有优化提示的代码 (hints),而运行时将选择这些提示的代码并在可能的状况下采用快速路径 (fast paths)。这里有三个主要的优化工做:
首先,在树级别,咱们注意到,在没有模板指令的状况下节点结构保持彻底静态,模板指令动态地改变了节点结构(例如:v-if 和 v-for)。若是咱们将模板划分为由这些结构指令分隔的嵌套“块”,则每一个块内的节点结构将再次变得彻底静态。当咱们更新一个块中的节点时,咱们再也不须要递归遍历该树-能够在平面数组中追踪该块内的动态绑定。这种优化经过将咱们须要执行的树遍历数量减小了一个数量级,从而避免了虚拟 DOM 的大部分开销。
其次,编译器会主动检测模板中的静态节点、子树甚至数据对象,并将其提高到生成代码中的 render 函数以外。这样能够避免在每次渲染上从新建立这些对象,从而大大提升了内存使用率并减小了垃圾回收的频率。
第三,在元素级别,编译器还会根据须要执行的更新类型为具备动态绑定的每一个元素生成一个优化标志。例如,具备动态类绑定和许多静态属性的元素将收到一个标志,该标志指示仅用于类型检查。运行时将获取这些提示并采用专用的快速路径。
综上所述,这些技术大大改善了咱们的渲染更新基准 (benchmarks),有时 Vue 3 占用的 CPU 时间不到 Vue 2 的十分之一。
(CPU 时间,即执行 JavaScript 计算所花费的时间,不包括浏览器 DOM 操做。)
框架的大小也会影响其性能。这是 Web 应用程序的惟一关注点,由于须要即时下载资源,在浏览器解析必要的 JavaScript 以前该应用程序是不可交互的。对于单页应用程序尤为如此。尽管 Vue 一直是相对轻量级的(Vue 2 的运行时大小压缩为 23 KB),但咱们注意到了两个问题:
首先,并非每一个人都使用框架的全部功能。例如,一个从不使用 transition 功能的应用仍需下载与 transition 相关的代码并解析。
其次,当咱们添加新功能时,该框架会无限期地增加。当新功能添加时,咱们不得不为包的尺寸比例折衷考虑。所以,咱们倾向于框架仅包含大多数用户使用的功能。
理想状况下,用户应该可以在构建时删除未使用的框架功能的代码(也称为“tree-shaking”),而且只打包使用的代码部分。这也将使咱们可以发布一部分用户会以为有用的功能,而不会增长其他用户的成本。
在 Vue 3 中,咱们经过将大多数全局 API 和内部帮助程序移至 ES 模块导出来,实现了这一目标。这使现代的打包工具能够静态分析模块依赖性并删除未使用的导出相关的代码。模板编译器还会生成友好的 Tree-shaking 代码,在模板中实际使用了该功能时才导入该功能的帮助程序。
框架的某些部分永远不会 Tree-shaking,由于它们对于任何类型的应用都是必不可少的。咱们将这些必不可少的部分的度量标准称为基准尺寸。尽管增长了许多新功能,但 Vue 3 的基准大小压缩后约为 10 KB,不到 Vue 2 的一半。
咱们还但愿提升 Vue 处理大型应用程序的能力。咱们最初的 Vue 设计着重于下降入门门槛以及温和的学习曲线。可是随着 Vue 愈来愈普遍地被应用,咱们了解到了更多有关项目需求的信息,这些项目包含数百个模块,而且随着时间的流逝由数十名开发人员维护。对于这些类型的项目,像 TypeScript 这样的类型系统以及可复用代码的能力相当重要,而 Vue 2 在这些领域的支持并不理想。
在设计 Vue 3 的早期阶段,咱们尝试经过提供对使用类编写组件的内置支持来改善 TypeScript 集成。挑战在于,咱们须要使类可用的许多语言特性(如类字段和装饰器)还是提案,而且在正式成为 JavaScript 一部分以前可能会发生变化。其涉及的复杂性和不肯定性使咱们对添加 Class API 是否真的合理这件事产生怀疑,由于它除了提供 TypeScript 集成稍好以外并没有其余功能。
咱们决定研究其余解决扩展问题的方法。受 React Hooks 的启发,咱们考虑过公开较低级别的响应式和组件生命周期 API,以实现一种更自由的编写组件逻辑的方式,称为 Composition API。无需经过指定一长串选项来定义组件,Composition API 容许用户像编写函数同样自由表达、组合和复用有状态组件逻辑,同时还提供了出色的 TypeScript 支持。
咱们对这个想法感到很是兴奋。尽管 Composition API 旨在解决特定类别的问题,但从技术上讲,仅在编写组件时才可使用它。在该提案的初稿(first draft)中,咱们预先暗示可能会在未来的版本中将现有的 Options API 替换为 Composition API。这致使了社区成员的大量反对,这使咱们得到了宝贵的经验,即如何清楚地传达长期计划和意图,以及更好地理解用户的需求。在听取了咱们社区的反馈以后,咱们对提案进行了彻底的从新设计,从而明确代表 Composition API 将是 Options API的补充和附加。修改后的提案更为积极,并收到了许多建设性的建议。
开发人员的配置文件多样性与用例的多样性相对应
Vue 拥有超过一百万的开发人员,有只了解 HTML / CSS 的基础知识的初学者、从 jQuery 迁移的专业人员、从另外一个框架迁移的资深人士、正在寻找前端解决方案的后端工程师以及处理大规模软件的架构师。开发人员配置文件的多样性与用例的多样性相对应:一些开发人员可能但愿将交互性扩展到旧版应用程序上,而另外一些开发人员则可能正在快速处理但维护需求有限的一次性项目中工做;在项目的整个生命周期中,架构师可能不得不该对大型的、多年的项目以及不断变化的开发商团队。
在咱们平衡各类因素的同时,Vue 的设计不断受到这些需求的影响,并不断适应这些需求。Vue 的口号是“渐进式框架”,封装了此过程当中产生的分层 API 设计。初学者可使用 <script>
引入 CDN,基于 HTML 的模板和直观的 Options API 来享受流畅的学习曲线,而专家可使用功能齐全的 CLI,render functions 和 Composition API 处理大型复杂的项目。
要实现咱们的愿景,还有许多工做要作-最重要的是,更新周边生态库、文档和工具以确保顺利迁移。在接下来的几个月中,咱们将继续努力,咱们火烧眉毛地想看看社区将经过 Vue 3 创造出什么。
1.看到这里了就点个在看支持下吧,你的在看是我创做的动力。
2.关注公众号前端食堂,你的前端食堂,记得按时吃饭!
3.本文已收录在前端食堂Github
github.com/Geekhyt,求个小星星,感谢Star。