在框架设计中寻求平衡
特别说明这是一个由 simviso 团队对 http://JSConf.Asia 中关于 前端框架设计取舍 相关话题进行翻译的文档,内容并非直译,其中有一些是笔者自身的思考。而分享者正是 Vue.js 的作者 @尤雨溪,Vue 仓库地址:https://github.com/vuejs/vue让我们一起来了解下在当前框架三足鼎立的局势下,如何直接透过框架本身了解到更多有关框架设计中的的权衡,以..
突然兴起看到知乎上的文章,特意记录下。
特别说明
这是一个由 simviso 团队对 http://JSConf.Asia 中关于 前端框架设计取舍 相关话题进行翻译的文档,内容并非直译,其中有一些是笔者自身的思考。而分享者正是 Vue.js 的作者 @尤雨溪,Vue 仓库地址:https://github.com/vuejs/vue
让我们一起来了解下在当前框架三足鼎立的局势下,如何直接透过框架本身了解到更多有关框架设计中的的权衡,以及 Vue 是如何进行取舍的。
视频地址:
【国外前沿技术分享-前端-中文字幕】尤雨溪:在框架设计中寻求平衡 上
【国外前沿技术分享-前端-中文字幕】尤雨溪:在框架设计中寻求平衡 下
视频翻译版权归 simviso 所有
一、前言
大家好,很高兴来到这里。你今天过得怎么样? OK
我很肯定你已经看过很多讲座了,也知道今天这次课程,希望这次不会太无聊,但是还是要给那些不认识我的人简单地介绍一下我自己。
我的名字叫 Evan You,我的推特账号是 Youyuxi,我的中文名字就是 Youyuxi。从2016年开始我就是一个独立的开源开发者。
也就是说我现在已经独立开源三年了,主要从事 Vue.js。
你们中有多少人在真正的使用它?
很好
它从 2013 年开始作为我的一个业余项目 ,我从 2016 年开始全职维护它。在多年的框架设计工作中,我学到了很多东西,这也给了我很多关于内部设计的观点。人们在构建正确的框架时做出的一些权衡决策。
你们中有多少人还记得 2013年 的那段时间,那时可能每天都会有一个新的 JavaScript 框架出现,NBC 有一个类似 40,50个框架的列表,这些框架都在构建相同的东西,Vue 差不多是从它们的时代开始的,当时我只是在研究一些现有的解决方案,并试图找出如果我构建这样的东西,我会怎么做。
但很明显,我对应该做什么的想法随着时间的推移而发生了很大变化。
但今天我将讨论其中的一些发现,特别是前端框架设计。
二、框架取舍
我敢打赌,很多人都在使用框架,即使你不使用 Vue,也可能使用 React、Angular 或其他框架。
很难想象,在没有这样工具的情况下就去构建一个复杂的前端应用程序。
当然,你仍然可以使用普通的 JavaScript 去做这些事,只是那样将花费我们更多的时间。
我猜当大多数人在面对这些框架的时候,会疲于对这些框架进行比较。
每当媒体给我推送关于框架比较的文章时,比如《2019最好用的7个新框架》,我通常会说:“咦~”
这不是因为说:“啊,我写了 Vue,我想让人用它,我不想听到有人说它的坏话”
而是因为大多数时候,这些文章只关注 github 的 star 数,npm下载量,stackoverflow 问题统计等这些可以随时随地通过 Google 查找到的内容。
但是,这些统计数据在某种程度上还是有用的,比如对于市场营销来说。
但是如果你尝试去做一个技术决策,并且尝试去和市面上已经成熟的技术去竞争的话,那么这些数字在某个阈值上的相关性会越来越小。
比如,我们在生产环境使用的东西,你知道的大部分可能都过了 10000 个 star 了。
但这个门槛真的那么重要吗?一个库到底有几千 star?
这并不重要,相反你应该更关心的是一些内部技术决策,比如导致这些框架的延迟发布的真实原因是什么。
所以在我们深入研究之前,我们先退一步思考下所有的这些框架的共同目标,我们都在努力实现的同一目标。所有框架的作者们都在朝着“让你们尽量能更高效地构建 Web 应用程序”这一目标而努力,那么为什么我们还要有这些不同比较的竞争的想法呢,这到底是好还是坏呢?
所以为什么我们有这么多不同的框架,而且每个框架都有很多的追随者呢?
就像 React 和 Angular 各自都有超过 50 万的用户。
我认为不能单纯的以一个好与坏来评价一个框架。
人们往往会问一些问题,比如哪个框架更好,请别再问这个了。
因为我们很难简单地去评定一个框架要优于另一个框架。
我们都知道软件设计在于取舍,事实上我们现在的前端框架设计有太多的地方需要进行取舍,尤其是在 Web 中。
因为 Web 是一个充满多样化元素的平台。
我们可以在 Web 上构建各种有趣的事情,无论是最简单的页面还是到你所每天使用的复杂程序。
因此,为了适应所有这些情况,框架制定者必须在多方面进行取舍。
今天我会将其中的一些拿出来聊聊,希望能对你在这块的看法有所帮助。但是由于时间原因,我肯定不能非常深入的去讲解每种情况。
所以,我只会专注其中的一部分。
第一:职责范围。从本质说是指这个框架可以为你做多少事情。
第二:渲染机制。当你在使用一个框架的时候,你会如何表达你的视图层,框架如何处理代码?它是如何将实际渲染东西展示到页面上的?
第三:状态机制。可变和不可变,脏检查和依赖追踪,响应式和类响应式。实际上,我没有时间去深入研究这个,这个或许可以在下次分享中好好谈论下。
三、职责范围
这次,我深入探讨下职责范围。看看一个框架能为我们做什么事情。
我们将它一分为二,从你们的角度来看,这个框架或库能做一些特有的小事情,从我们的角度来看,我们会有很多整体性的考量,拿框架来说,我会尽量提供尽可能多的特性。
1、React & Angular
举一些大家可能比较熟悉的代表性的例子,React 接近于原生库,Angular 则提供更多抽象概念。
很少有人会去说清楚原理,这个库(React)专注于提供一个非常基础的 UI 模型,它专注于提供更底层的原生实现,基于此你可以构建出一套属于自己的抽象。
有意缩小职责范围(设计理念),这也是React整个生态系统非常活跃的原因所在,React的社区环境就像个商城(系统),围绕着 React 的核心模型自底向上建立起来的一整套生态系统。
另外一方面,像 Angular 以及其他的一些框架,如 Ember、 aralia 这种,则更像是大教堂吧。
它们则是自上而下进行设计的,在设计过程中,用户可能会遇到的问题都被考虑在内。
例如你们在日常开发中会经常遇到的表单验证,动画效果等等。
为了让框架可以给我们提供一个解决方案,在这个框架设计之初,我们就要以自上而下的方式来对它进行设计,即我们需要去思考如何将所有事情放到一起去工作。
有意扩大职责范围(设计理念),这个设计理念就是当你尝试解决一个问题时,你在框架内就能找到解决方案。
我们把这称之为大小职责范围(这里指两种设计理念),而这也没有好坏之说。
我想再次强调下。
2、Small Scope
优点
小的职责范围(设计理念)它的好处在于,刚开始的时候需要理解的概念很少。
而且它具有更好的灵活性。由于框架、库只提供了部分底层的原生实现,所以当你需要一个组件类的话,它得有一些 Props,然后你可以通过 Props 来传递数据,返回 VDOM 节点。
最重要的是,你可以构建任意复杂的系统。
React 有一个非常活跃的生态系统,我们能看到它拥有一些非常灵活的工具,然后,我们可以基于它们发挥出更大的创造力,而这些很棒的想法都来自 React,React 社区。
另外,有意缩小职责范围,也可以让团队拥有更小的维护层面,以便他们可以专注于他们认为重要的事情。
这样团队可以专注于探索一些比较有创意的点子,这也是为什么 React 可以花这么多时间来做 concurrent mode、suspense、React Hooks 以及在过去几周或几个月内一直在创造的所有的这些有趣的东西。
实际上他们在这些事情的开发上已经花了好几年了,只不过因为它们的粒度比较小,正是因为这样他们才能更加专注于这个事情上。
缺点
当然,小范围的设计也是有一些明显的缺点的。
首先第一点,当你想用一个简单的概念去解决一个本来就很复杂的问题的时候你需要做更多的钻研工作。
我特别喜欢 Steele 这个家伙在一场演讲中说的一句话:“培养一种语言”。在演讲期间,他给自己制定了一条规则,他在演讲期间必须使用单音节词,如果你想用任何类似于多音节词的话,他必须先用单音节词来定义它。
他只有非常有限的、非常原始的一些东西,去构建一些复杂的想法。
所以你可以想象一下那次演讲是怎么回事。
在他每次想要说出自己喜欢的一句话之前,他都要翻出一些幻灯片然后找出一堆单词,然后继续。这有点像用非常低级的原始语言去构建一个非常复杂的生产级别的应用程序。
你必须构建大量的抽象概念,以便在此过程中提高自己的效率。
正因为如此,模式自然会随着时间的推移而出现。
当我们说 React 是真的容易上手的时候,我们是否有些忽略了你或多或少需要学习 Redux 的事实。在你认为自己是一名真正的 React 开发人员之前,你必须先去了解 HOC 高阶组件、render props。然后现在,你又要去学习 Hooks 以及在 JS 中使用 CSS 的各种方式。
尽管这些模式随着时间的推移而出现,但它们却变成了半需要的状态。如果你不了解这些,你真的可以称自己是一名 React 开发人员吗?
而这些东西往往没有官方文档。如果你去 React 官方网站,它是不会告诉你某个 CSS 和 JS 的解决方案的,你必须自己去研究并学习它。
这就是为什么使用者真的需要有自主学习能力的原因所在。
现如今,生态系统发展太快可能会导致碎片化和使用者的不断流失。我相信早期 Flux 思想的追随者,会发现每天都有一个新的 Flux 方案具体实现,然后每个 Flux 的具体实现都会对应新的 CSS/JS 的实现。
这样很好,同时好处就是总会有新的想法出现。同样,我们也在努力寻找最佳方案。
但是,这对于那些只想跟在后面去使用的人来说是不好的,你会不断的有FOMO思想(Fear of Missing Out,害怕错过),总是怕错过下个最好的东西。
3、Large Scope
我们已经说完了小职责范围(设计理念)的缺点。现在让我们来谈谈大职责范围的一些优点。
优点
最明显的优点是可以通过构建抽象概念来解决最常见的问题。
如果你只是想做一些开发,我只需要一个路由、一些动画以及一个 HTTP 客户端来获取一些数据。
像 Angular 这样的框架则提供了所有你需要的东西,来实现这一目标。
事实就是你不必到处查资料,你只要照着它的文档去用框架,你就可以搞定这些事。
集中式的设计确保了它本身与其生态系统的一致性。
当你遇到一个具体问题的时候,你不必去找一些不同的解决方案,你只需要看看框架它让你做什么,最大的可能就是它(框架)对此已经有解决方案。所以你压根不必去挖掘所谓的10种不同的解决方案,然后找出对你案例最佳的那一个。
缺点
现在来说下缺点,这样的缺点就是会有一个更高的学习门槛。
为了将一个像素放到屏幕中,你必须跳过一些步骤才能做到,这对于初学者来说是一个很大的障碍。对于那些不适应的人,我这里不说那些适应的人哈,如果你没有类似使用 Java 或者 C# 等语言经验的话,而是仅仅只学过 HTML/CSS 以及 JavaScript 的话,当你看到 Angular 文档的时候,你可能会觉得有点不可思议。
对我来说也是如此。
然后,如果内置的解决方案是不适合当前用例的话,它则会变得不灵活。有时候你可能觉得我只是想用另外一种方式来做,但是我却没方法来将其替换。
最后,一个职责范围大且成型的框架会使得引入一些底层新想法的成本更高,因为太多的地方都要保持其一致性。
然后当你想尝试用一个底层想法的时候,它会影响到你项目中的每个组件(牵一发而动全身)。
所以要做这种 “改变”,就变成了一件很困难的事情。而如果你在 React 的生态系统中说,我引入 Hooks 会让 Redux 更加冗余,那么朋友,这并不是一个真正的问题,因为 React 它的核心团队实际上并不会负责这些解决方案。
就是这样,okay。
4、渐进式框架 Vue
这正是 Vue 所处的位置。但在我们深入了解 Vue 现在正在做的事情之前,我想强调的是,我并不是说 Vue 比这两个框架都好。
因为处于中间位置不一定就是最好的。如果 NG 和 React 在某个功能的封装程度上差异很大,Vue 要做的就是去缩小差异,然后你会发现实际上 Vue 并没有做到最佳。
所以,这就好像我们稍微推迟去寻求我们所认为的最佳平衡点。而每一个选择都会适用于不同人群的需要。
它并不像一件东西一样,可以适用于所有人。
所以我所说的 Vue 在职责范围这个问题的处理方式上,你可能知道我们都叫 Vue 是一个渐进式框架。
优点
职责范围渐进意味着框架使用分层设计,它允许以渐进的方式选择特性。也就是说,如果你不需要路由,如果你不需要状态管理,甚至如果你不需要构建步骤。你可以使用没有任何特性的 Vue,你只需要将 vuejs 拉到你的页面中,然后你就可以立即开始做一些事情。
对于初学者来讲,一个需要翻越的学习障碍,就是刚开始学习时就要求你从屏幕中获取一个像素并移除它。
所以,低的学习门槛对我们来真的很重要,这也是 Vue 的一个使命:允许更多的人参与 Web 开发,允许人们学习它(Vue)并专注于开发,而不是让你学习一堆在你当前开发可能不需要的概念。
但是对于当前的这些问题,我们仍然有通过文档去提供解决方案。当你的用例变得更复杂的时候,当你要构建更复杂的东西的时候,这时你意识到自己需要一个路由。然后你就开始翻阅文档,你会发现,okay,我确实可以使用路由去做这个。
但与此同时,路由它又并不是必需的。并且,如果你愿意的话,你自己也能实现一个路由,因为你能看到 Vue 的路由是如何构建的,并且它的核心实现是非常干净的, 所以如果你愿意的话,是可以用自己的方式去构建一个的。
缺点
它并不完美,因为作为中间者,我们需要去权衡两者的利弊。
所以首先,尽管我们会采用新增模块(集成 router、vuex 等),但我们仍然需要负责维护它们(router、vuex 等)
所以,我们分享了大职责范围下统一维护面的问题,我们要想从根本上改变一些东西,我们必须确保整个生态系统的一致性。
这个维护负担几乎与大职责范围相同,同时也因为我们提供了这些预置的解决方案。
我们的生态系统可能不会像小职责范围那样多样化。因为小职责范围喜欢把问题抛给社区。而在我们的案例中,很多用户很满意我们的解决方案,同时自己也不用再花费时间去找答案。
这就是职责范围的问题,希望你现在已经有所了解了,我认为这就是 React、Vue 和 Angular 之间最根本的区别。
这是它的准确定位,也定义了我们不同的用户群。
很多时候,我们都是在有意决定所扩展的领域。作为框架设计者,我们知道我们正在驾驭不同的领域。这是件好事,因为在整个阶段,不同的开发人员需要不同的解决方案,以及拥有覆盖整个主要框架的规范,确保每个人都得到他们想要的。
四、渲染机制
OK,现在我们来谈谈渲染机制。
也就是说你该如何通过框架来表现自己的UI结构以及该框架是如何进行渲染的。
首先,从操作层面来讲它真的很复杂,它不仅仅是一件事,它包含了很多层面。
简单点来讲,可以将它看成 JSX 与 Templates,即动态渲染函数和基于静态字符串的复杂的 Vue 表达式。然后就是表现力和原生性能,以及运行时调度和提前优化。
有些人对此会有很强烈的意见,但我个人认为他们本质上是很相似的,他们只是对同一个底层理念的不同策略表达。
可以说更多的是技术上的权衡。
一方面,当然是 JSX React 以及所有使用 VDOM 的 react-like 库,比如 pre-act、 stencil, infernal 等。
另一方面,则是基于模板的解决方案。我稍后会讨论 Vue,但更具代表性的基于模板的解决方案有 Svelte,还有就是 ember。
可以看到,那个 logo 实际上是 glimmer.js 的 logo,也就是说,glimmer 是 Ember 里的渲染引擎,同样也是 Angular 的渲染引擎。
这些主要是基于模板的,它们将模板编译成相对较低级别的指令来进行内容渲染。
1、JSX / VDOM 优点
现在我们来谈谈 JSX 和 VDOM 的一些优点。
我们喜欢 JSX 或 VDOM 最重要的原因就是它们具有 JavaScript 的完整表现力。
你无须在意语法,你有一种可供你使用的语言,你就可以有完整的体验,你可以做任何你想做的事。
不仅这样,你还可以在你的组件上构建任意复杂的逻辑。
它真的很强大、不做约束,也因为这个特点,很多人喜欢上了 React 。
它还允许你在渲染组件时将视图层视为数据。它会返回一些东西,返回节点表示当前状态的VDOM 节点,这个数据可以用于很多有意思的地方。
它对于我们构建测试方案很有用,你可以根据虚拟Dom获取快照,你可以将它渲染到需要替换的目标,也就是我们一直在做的事情,比如将它渲染到终端、PDF、Canvas、WebGL,以及任何你能想到的你可以渲染的东西。
因为视图就是是数据,而你可以对数据做任何操作。
2、JSX / VDOM 缺点
目前,VDOM 本身成本真的很高。
想一想,当我们刚出来的时候,很多人都认为,这不是很慢吗?答案是 Yes,我们很慢,但速度却足够快!
但是,仍然从纯粹的技术角度来看,你做了很多多余的工作。想想这个简单的模板,它只需要更新其中单个消息绑定就可以完成大量的工作。
我们必须遍历整个 VDOM 节点并且做 Diff,在这个过程中,一直以递归的方式向下传递,直到你以某种方式更新它。
因此,标准 VDOM 不同的代价是相对于视图的总大小,而不是可能改变的节点数量。
即使你只有一个节点,也可能会触发 这个VDOM 的 Diff 算法。
正是由于渲染函数的动态特性使得它难以优化。
关于动态性,我的意思是你可以写这样的代码。
你可以使用一个 for 循环来构建一个 children 数组,然后将它交给你的父节点,以及接下来进行你想要做的其他事情,也就是说你可以先创建一个父节点,然后通过往它的 children 中添加元素来进行改变。
你在使用 JavaScript 的时候,编译器不可能hold住所有可能发生的事情,因为 JavaScript 太过于动态化。
在这块我们已经做了很多尝试,但从本质上来说,我们很难通过这种方式对其提供安全的优化。
因为它不是你简简单单做足够多的条件预断就可以的,你对用户意图做的预判越多,代码越容易优化,这对于Javascript来讲实在太难了。
最后,React 对于这块的解决方案就是不把重心放在加快 VDOM 本身的速度,而是如何提高感知性能。
因此,React 引入了运行时调度并发时间切片的概念,但是这种运行时调度方案,整个 fiber,几乎和我们管理自己的堆栈一样,比如进入和退出渲染,所有的东西都需要很长的运行时间。
这意味着无论你何时加载 React,都必须去加载那些用于处理复杂的运行时调度工作的所有必需代码。
这就像加载几个20、30KB 大小的 JavaScript 一样。
反过来,这也会让你的初始化加载受到一点影响。
3、模板编译优点
另一方面,如果你是在模板中编译的渲染代码,通常它可以生成一个更加直接的渲染指令,并且具有更好的原生性能。原因就是:根据定义,模板是一种非常有约束的语言,你只能以某种方式去编写模板。
例如,当你写出这样的代码的时候,我们可以立刻告诉你,这些 p 标签的顺序是不会变的,这个 id 是不会变的,这些 class 也不会变的,唯一会变的就是这个。
静态(编译)和非常严格的限制实际上是允许编译器对你的意图做更多的预判,从而给它更多的空间去做执行优化。
我们来看看 SVELTE 编译代码时会做什么。
其他所有内容都是静态的,只有 name 可能会发生改变,这个 p 是一个 update 函数,它唯一做的事情就是当 name 发生变更的时候对它(name)进行更新。
将 SVELTE 与 VDOM Diff 算法所做的事情相比较的话,它只有到达一定量级才会更快。
所以说,根据策略的不同,模板编译或者基于通用编译的方法也可以使 runtime baseline 更轻量,因为它不需要所有的复杂运行时调度来尝试让事情看起来更快,因为它本身已经很快了。
所以 SVELTE 可以产出一个非常轻量的输出,而不需要一个很重的 baseline runtime 来适应所有可能的 runtime 行为。
4、模板编译缺点
现在来看看模板编译的缺点。很显然,你会受限于模板语法,从而失去 JavaScript 的表达能力。
所以,当你想去构建一个真正复杂的组件的时候,你会想我要可以在模板里这样做多好,然而编译器对此并不支持。
很不走运,如果你选择完整的编译路线,那就没有退路,因为级别越低的编译输出,实际上你越难将你的自定义操作与它进行挂钩。
就好比 opa 编译器,你是无法深入到汇编去看看为什么会这样,就好比你无法使用 C 语言去调试你的汇编代码。
更轻量的 runtime、更轻量的 baseline runtime,也可能是以每个模板更详细的输出作为代价。因为当你试图去生成尽可能高效的代码时,有时你必须直接在输出的时候编码更多信息。
例如,SVELTE 生成的代码其实是以命令式的形式通过逐行插入创建了所有元素,并且它们有一个单独的函数进行更新操作。
相比之下,基于 VDOM,结果是你只需要一行,而这(一行)只是一个返回 VDOM 结构的表达式。
如果在运行时编译,则会产生运行时编译成本。
因此,对于生产用例来说,最有可能的情况是你需要用户事先编译,这样对于构建步骤来说是一个硬性要求,这是不可避免的,要么在运行中进行编译,要么在预构建中进行编译,其中涉及我们现在或多或少习惯的所有node.js工具链。但如果你能避免的话,对于初学者来说是件好事。
OK,因此,Vue 又一次夹在中间,我想再次强调,这不是说 Vue 是最好的。
但是,Vue 的渲染机制独特之处在于,如果你真的将模板结合到 VDOM 中,那么我们可以同时拥有 VDOM 和模板编译。
所以我们可以充分利用这两点。
我们有做过性能测试:在编译步骤中产生的特别优化,我们不做渲染功能,稍后我将详细介绍这一点。
在 vue2.x 版本,我们实际上还没有充分利用这个机会,当前 vue 2.x 中的 VDOM 性能这块表现平平。
但我会谈下 3.0 对这点所做的事,让它可以更快。
还有表现力,你可以跳过模板层直接进入渲染函数,直接利用JavaScript来执行任意复杂的逻辑。
因此,当你感觉自己受到模板约束时,这会为你提供一条出路。
缺点是,尽管我们现在确实很快,我们可能永远不会比 SVELTE 感觉起来快。因为 SVELTE 的输出是最普通的 JavaScript。然而,为了兼容手写的渲染函数,Vue 仍然需要维护 VDOM,这样一来常量则必不可少。另一方面,它也会产生分歧,即我到底应该使用哪一种方式?
因此,很多用户虽然可以使用渲染函数,但他们可能从未使用过它。
现在让我们把它放到我们通过文件做的事情上,将 Vue 的模板编译成 VDOM,其运行速度比普通的 VDOM 要快。
这就是我们刚才已经讨论过的,这个模板只有一个节点会改变。理想状态下我们只需直接更新 message 字符串,节点结构是静态的,并且从来不会发生变化,只有一个动态节点。
所以,如果我们研究这个模板可以看出它是个非常简单的例子。
当我们有类似于 v-if 这样的东西时,它会变得有点复杂,我们称之为 JSX 中的结构指令,它相当于是根据条件返回不同结果判断的三元表达式。
现在,这会创建一个动态节点结构,因为该节点可能存在或可能不存在。
为了处理这种简单的 VDOM Diff 算法,假设节点列表已经改变,那么我们需要对两个子数组进行 Diff 操作。
但是如果我们尝试将其拆分,会看到 v-if 将模板拆分为两个嵌套块。
我们来思考一下,如果将 v-if 本身看作一个节点,外部块则会有一个静态节点内容、节点结构。
在 v-if 内部,它也是静态的。我们有两个静态块,在每个块内,你无需对节点顺序进行 Diff 操作,你唯一要做就是这个块内部进行数组的扁平化操作。
同理,对于每个 v-for 迭代我们也可以将其看成一个静态块。
因此,如果你有更多像 v-if 、内嵌 v-for 的写法,你只是在进一步将代码拆分成嵌套块。
所以,我们最终得到一种我称之为 Block Tree(区块树)的东西,这只是一种玩法。
但 Block Tree 是一个嵌套块的块列表,因为每个块中都有一个完全静态的节点结构,所有没有必要使用递归到下层去对子列表进行 Diff 操作。
在每个块中,你只有一个单一扁平化数组节点可能会发生改变,我们还提供了其它组织上的提示。例如,如果一个节点只有一个动态 class 绑定,我们有条捷径,即你只需直接设置 class,然后就可以继续执行,而不必对 Props 进行 Diff 操作。
所以,对于同一模板的 Diff,vue2.x 版本和 vue3.x 版本的做法会有明显的区别。vue2.x 版本我们需要做一个完整的 Diff 操作,vue3.x 版本我们就只需通过使用一个单一扁平化数组(包含一个动态文本节点),而你唯一需要做的事情就是比较文本是否发生了改变。
对此,我们做了一个简单的基准测试(benchmark), 做 1000 个 v-for 列表迭代,每个块有 12 个 dom 节点,总共 12000 个 dom 节点,每次迭代都会动态绑定一些类或者文本。
然后我们在页面上做了 4000 个动态绑定,然后对其更新。我们做了 100 次运行,在目前 2.6 的版本,更新时间要 36ms,而在目前 3.0 的版本中,使用新的编译策略,只需要大概 5.4ms,比之前快了6倍多。
注意,(数据)仅限于这个基准测试。真实的应用中你可能会有一个不同的数字,但或多或少,它都会更快,这是一个基准。
五、状态
然后,在状态机制这块,我可能没有时间去真正深入研究这个问题,它可能会是另外一个演讲的话题。
六、总结
但是还是总结一下,当你试图去设计一个框架时,最佳平衡点在哪?
或许这个问题应该重新表述下。是否存在一个完美的平衡点?它又是否是一个单一的完美的平衡点,甚至是以 JS 开发人员作为一个整体的最佳平衡点?
因为像我们所有人一样,都在努力去优化我们正在构建的一些特定又不同的东西。
比如说 SVELTE,它的优势在于当你构建一些小的东西时,它可以产出非常轻量级的代码。它也非常快,消耗内存也非常少,所以它甚至可以像嵌入式设备一样使用。
但是如果在你自己的使用场景中,你可能会有一个更复杂的实现,你会有更多的组件,你想要 JavaScript 的表现力,同时你也需要模板提供更多的性能,那么(这种场景下)你可以使用 Vue。如果你不是很在意一些极端的性能,然后说我就是喜欢 React 的生态系统,那么你可以使用 React。
你也可以选择所有这些(框架)。我认为这样很好,框架领域能像一个多维空间,有多个不断变化的实体,就像把每个框架都想象成一个试图寻求平衡点的实体。相信我们总是会有很多人去努力找出什么是最佳的做事方式。
作为一个开发人员,你可能会在这些实体之间游移不定,然后你可能会被其中的一个吸引过去,有时你有可能只是在周围跳来跳去,然后试着去找出最适合你的一个。我认识这是件好事。
但是作为一个使用者,尝试在这个多维空间中找到正确方法是非常困难的。
但是如果你想正确的选择框架,难的就是你必须了解框架所做的一些内部权衡,你必须知道这个框架朝着哪个方向发展,并且知道它与你构建的东西是否一致。
希望通过这次演讲所阐述的话题能够帮助到你,即当你尝试在未来选择框架、或者当你告诉别人应该如何选择框架时,能给予你帮助。
谢谢。
作者:qiangdada
更多推荐
所有评论(0)