当前位置: 首页 > Web前端 > vue.js

《汉译》Vue3的诞生

时间:2023-03-31 23:30:31 vue.js

欣赏:??????口味:麻辣小龙虾烹饪时间:10min本文已收录于Githubgithub.com/Geekhyt,感谢Star。原文地址:https://increment.com/frontend/making-vue-3/译者:TongOubaine由于时间有限,文中翻译错误的请王海涵指出。想要感受原汁原味的,请点击上面的链接。恭喜裕达!Vue3之路EVANYOU重写下一个Vue.js大版本的体验在过去的一年里,Vue团队一直致力于Vue.js的下一个大版本,我们希望在2020年上半年发布.(截至撰写本文时,这项工作仍在进行中。)Vue的新主要版本的想法形成于2018年底,当时Vue2代码库已有两年半的历史。这在一般软件的生命周期中听起来可能不是很长一段时间,但在那段时间里,前端环境发生了翻天覆地的变化。有两个主要因素促使我们开发(重写)Vue的新主要版本:首先是主要浏览器对新JavaScript语言功能的普遍支持。其次,随着时间的推移,当前代码库中的设计和架构问题已经暴露出来。为什么重写?(改写初衷)利用新的语言特性随着ES2015的标准化,JavaScript(正式名称为ECMAScript,缩写为ES)得到了极大的改进,主流浏览器终于??开始对这些新特性提供良好的支持。特别是,其中一些新功能为我们提供了大幅提升Vue.js性能的机会。其中最值得注意的是Proxy,它允许框架拦截对对象的操作。Vue的一个核心特性是能够监听用户定义的状态变化并响应地更新DOM。Vue2通过用getter和setter替换反应对象的属性来做到这一点。切换到使用Proxy可以消除Vue的一些现有限制,例如无法检测新属性添加并为它们提供更好的性能。但是,Proxy是一种本地语言功能,在旧版浏览器中并不完全受支持。为了使用它,我们意识到我们必须调整框架的浏览器支持范围,这是一个破坏性的变化,只能在新的大版本中发布。解决架构问题要解决当前代码库中的这些问题,重构的风险比较高,几乎等同于重写。在维护Vue2的过程中,由于现有架构的限制,我们积累了很多难以解决的问题。例如,模板编译器的编写方式使得正确的源映射支持非常具有挑战性。此外,虽然Vue2在技术上允许构建针对非DOM平台的更高级别的渲染器,但我们必须分叉代码库并复制大量代码才能实现这一点。要修复当前代码库中的这些问题,重构是有风险的,几乎等同于重写。与此同时,我们积累了各种模块内部隐式耦合和似乎不属于任何地方的浮动代码的技术债务。这使得孤立地理解部分代码库变得更加困难,而且我们注意到贡献者对重要更改没有信心,重写将使我们有机会在考虑这些因素的情况下重新考虑代码组织。初始原型制作阶段我们于2018年底开始制作Vue3原型,最初的目标是验证解决这些问题的可行性。在这个阶段,我们主要着眼于为进一步发展打下坚实的基础。切换到TypeScriptVue2最初是用纯ES编写的。进入原型设计阶段后不久,我们意识到类型系统对于这种规模的项目非常有帮助。类型检查大大降低了在重构过程中引入意外错误的机会,并帮助贡献者更有信心地进行重要更改。我们采用了Facebook的Flow类型检查器,因为它可以逐渐添加到现有的Plain-ES项目中。Flow在一定程度上有所帮助,但我们并没有像我们希望的那样充分利用它。尤其是不断的变化,让升级变得很痛苦。与TypeScript和VisualStudioCode之间的深度集成相比,对集成开发环境的支持也并不理想。我们还注意到,用户越来越多地同时使用Vue和TypeScript。为了支持它们的用例,我们必须在不同类型系统下与源代码分开编写和维护TypeScript声明。切换到TypeScript将使我们能够自动生成声明文件,从而减少维护负担。解耦内部封装我们同样采用monoreposetupframework的内部封装,每个封装都有自己独立的API、类型定义和测试用例。我们想让这些模块之间的依赖关系更加明确,让开发人员更容易阅读、理解和进行任何更改。这是我们努力降低项目的贡献壁垒并提高其长期可维护性的关键。设置RFC流程到2018年底,我们有了一个使用新的响应系统和虚拟DOM渲染器的工作原型。我们已经验证了我们想要进行的内部架构改进,但仅包括面向公众的API更改草案。是时候将它们变成具体的设计了。我们知道我们必须提前计划。Vue的广泛使用意味着重大更改可能会导致用户的大量迁移成本和潜在的生态系统碎片化。为确保用户能够就重大变更提供反馈,我们在2019年初采用了RFC(征求意见)流程。每个RFC都遵循一个模板,章节中突出了动机、设计细节以及权衡取舍。由于这个过程发生在GitHub存储库中,提案以拉取请求的形式出现,因此讨论自然会在评论中展开。RFC过程已被证明是非常有用的,它是一个全面的思想框架,使我们能够充分考虑潜在的变化,并允许我们的社区参与设计过程并提交经过深思熟虑的功能请求。更快更小Performanceiscriticaltofront-endframeworks性能对前端框架至关重要。虽然Vue2具有出色的性能,但仍有机会通过尝试新的渲染策略重写来进一步提高性能。克服虚拟DOM瓶颈Vue有一个相当独特的渲染策略:它提供类似HTML的模板语法,但将模板编译成返回虚拟DOM树的渲染函数。该框架通过递归遍历两个虚拟DOM树并比较每个节点上的每个属性来确定需要更新真实DOM的哪些部分。由于现代JavaScript引擎执行的高级优化,这种有点蛮力的算法通常很快,但更新仍然涉及大量不必要的CPU工作。当您查看主要包含静态内容和只有少数动态绑定的模板时,效率低下尤其明显——仍然需要递归遍历整个虚拟DOM树以找出更改的内容。幸运的是,模板编译步骤让我们有机会静态分析模板并提取有关动态部分的信息。Vue2通过跳过静态子树在某种程度上做到了这一点,但编译器架构太简单了,无法进行更高级的优化。在Vue3中,我们使用适当的AST转换管道重写了编译器,这允许我们将编译时优化编写为转换插件。有了新架构,我们希望找到一种开销尽可能小的渲染策略。一种选择是放弃虚拟DOM并直接生成命令式DOM操作,但这将消除直接编写虚拟DOM渲染函数的能力,我们发现这对高级用户和库作者非常有价值。此外,这将是一个突破性的变化。其次,最好的解决方案是消除不必要的虚拟DOM树遍历和属性比较,这往往会在更新期间产生最大的性能开销。为了实现这一点,编译器(compiler)和运行时(runtime)需要一起工作:编译器分析模板并生成带有优化提示的代码(提示),运行时将选择带有这些提示的代码并在可能的时候在快速路径(fastpaths)下。这里有三个主要的优化工作:首先,在树级别,我们注意到节点结构在没有模板指令的情况下保持完全静态,模板指令动态地改变节点结构(例如:v-if和v-for)。如果我们将模板划分为由这些结构指令分隔的嵌套“块”,则每个块内的节点结构再次变得完全静态。当我们更新块中的节点时,我们不再需要递归遍历树-可以在平面数组中跟踪块内的动态绑定。这种优化通过将我们需要执行的树遍历次数减少一个数量级来避免虚拟DOM的大部分开销。其次,编译器主动检测模板中的静态节点、子树甚至数据对象,并将它们提升到生成代码中的渲染函数之外。这避免了在每次渲染时重新创建这些对象,大大提高了内存使用率并降低了垃圾收集的频率。第三,在元素级别,编译器还根据需要执行的更新类型,为每个具有动态绑定的元素生成一个优化标志。例如,具有动态类绑定和许多静态属性的元素将收到一个标志,指示它仅用于类型检查。运行时将获取这些提示并采用专用的快速路径。总而言之,这些技术极大地改进了我们的渲染更新基准测试,与Vue2相比,有时Vue3花费的CPU时间不到十分之一。(CPU时间,即执行JavaScript计算所花费的时间,不包括浏览器DOM操作。)最小化BundleSize框架的大小也会影响它的性能。这是Web应用程序唯一关心的问题,因为资源需要即时下载,在浏览器解析必要的JavaScript之前,应用程序无法交互。对于单页应用程序尤其如此。尽管Vue一直比较轻量级(Vue2的压缩运行时大小为23KB),但我们注意到两个问题:首先,并不是每个人都使用框架的所有功能。例如,一个从不使用过渡功能的应用程序仍然需要下载和解析与过渡相关的代码。其次,随着我们添加新功能,框架会无限增长。当添加新功能时,我们不得不在包的大小比例上做出妥协。因此,我们更喜欢框架只包含大多数用户使用的功能。理想情况下,用户应该能够在构建时删除未使用的框架功能(也称为“tree-shaking”)的代码,并且只打包使用的代码部分。这也将使我们能够发布一部分用户会觉得有用的功能,而不会增加其余用户的成本。在Vue3中,我们通过将大部分全局API和内部助手转移到ES模块导出来实现这一点。这使现代打包工具能够静态分析模块依赖关系并删除未使用的与导出相关的代码。模板编译器还会生成友好的Tree-shaking代码,仅当函数在模板中实际使用时才导入函数的帮助程序。框架的某些部分永远不会进行tree-shaking,因为它们对于任何类型的应用程序都是必不可少的。我们将这些基本部分的度量称为基线大小。尽管添加了许多新功能,但Vue3的基本大小压缩后约为10KB,不到Vue2的一半。解决规模需求我们还想提高Vue处理大型应用程序的能力。我们最初的Vue设计着重于低门槛和平缓的学习曲线。但是随着Vue被更广泛地采用,我们更多地了解了包含数百个模块并由数十名开发人员长期维护的项目的需求。对于这些类型的项目,像TypeScript这样的类型系统和重用代码的能力是至关重要的,而Vue2在这些方面的支持并不理想。在设计Vue3的早期阶段,我们试图通过提供对使用类编写组件的内置支持来改进TypeScript集成。挑战在于,我们需要使类可用的许多语言特性,例如类字段和装饰器,仍然是提案,并且在正式成为JavaScript的一部分之前可能会发生变化。所涉及的复杂性和不确定性让我们质疑添加类API是否真的有意义,因为它只是提供稍微更好的TypeScript集成。我们决定研究解决缩放问题的其他方法。受ReactHooks的启发,我们考虑公开一个较低级别的反应式和组件生命周期API,以更自由的方式编写组件逻辑,称为CompositionAPI。CompositionAPI不是通过指定一长串选项来定义组件,而是允许用户像编写函数一样自由地表达、组合和重用有状态的组件逻辑,同时还提供出色的TypeScript支持。我们对这个想法感到非常兴奋。尽管CompositionAPI旨在解决特定类别的问题,但从技术上讲,它仅在组合组件时可用。在提案的初稿中,我们提前暗示现有的OptionsAPI可能会在未来的版本中被CompositionAPI取代。这导致了社区成员的强烈反对,这为我们提供了宝贵的经验教训,让我们了解如何清楚地传达长期计划和意图,并更好地了解用户需求。在听取了我们社区的反馈后,我们完全重新设计了提案,明确表示CompositionAPI将作为OptionsAPI的补充和补充。修改后的提案更加积极,收到了很多建设性意见。寻求平衡开发人员配置文件的多样性与用例的多样性Vue有超过一百万的开发人员,从只了解HTML/CSS基础知识的初学者到从jQuery迁移的专业人士,从另一个框架迁移的老手,后端工程师寻找前端解决方案,以及处理大型软件的架构师。开发人员配置文件的多样性对应于用例的多样性:一些开发人员可能希望将交互性扩展到遗留应用程序,而其他人可能正在处理快速处理但维护需求有限的一次性项目;架构师可能不得不在项目的整个生命周期中处理大型的、多年的项目和不断变化的开发团队。当我们平衡各种因素时,Vue的设计不断受到这些需求的影响并适应这些需求。Vue的标语是一个“渐进式框架”,它封装了由此过程产生的分层API设计。初学者可以使用