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