学习
实践
活动
工具
TVP
写文章

Brahmos:一个更小、更快的类React UI框架,支持并发渲染

本文要点

  • 虽然近年来用于构建交互式Web应用程序的新UI框架的出现速度有所放缓,但在2020年,我们还是看到了一些主要关注简单性和性能的新框架;
  • Brahmos旨在用一种不一样的、更快的方法来实现已知的React API,还利用了JavaScript的一个标准特性:模板字面量。因此,Brahmos努力实现了React的钩子、上下文、并发模式等等;
  • Brahmos是为数不多实现了React并发模式API的UI框架之一。其他框架可能还在等待,或者完全放弃该特性。
  • 虽然新JavaScript框架的标准不断被该领域内占主导地位的框架抬高,但新进者表明,仍有创新的余地。

Sudhanshu Yadav发布了Brahmos,一个新的用于开发交互式Web应用程序的前端框架。Brahmos努力遵循最新的React API,不过采用了完全避开虚拟DOM的实现。Brahmos采用了模板字面量,这是在ES6/ES2015中引入的一个新的JavaScript语言特性。好处是通过更少的框架代码和更快的DOM更新计算来提高性能。

Brahmos目前实现了大部分React API,包括功能组件、钩子、上下文、refs、forward refs、suspense、并发模式等等。相关的示例代码可在这里中找到。

Brahmos是一个新的前端框架,旨在提高React的性能。Preact试图通过更少的代码来实现性能提升,但面向的是DOM渲染。与React不同,但与Preact一样,Brahmos不适用于非基于DOM的输出设备(移动设备、pdf、webGL等)。

Brahmos一个有趣的地方是它实现了并发模式,这个功能在React中开发了好几年,到现在还没有正式发布。

Brahmos的短期目标是重用现有的React组件(目前尚未实现)。此外,Brahmos的性能测试基准也有待制定。

InfoQ就该框架的基本思想、目标、价值和路线图采访了Sudhanshu Yadav。有兴趣深入了解的读者也可以在网上观看演讲视频。

InfoQ:你能向我们的读者介绍一下你自己吗?

Sudhanshu Yadav:我是HackerRank的前端架构师。我很喜欢去了解我所使用的技术的内部原理,喜欢探究事物的运作原理。我还组织了一个研讨小组,讨论不同技术的内部原理。除此之外,我还喜欢探索架构、模式、工具和系统设计。 我是开源软件的忠实信徒,我开发了Brahmos、react-number-format(每月有1百万的npm安装量)、packagebind和其他开源工具和库。

InfoQ:你最近发布了Brahmos,一个实现了现代React API的前端UI库。这让Brahmos与其他React库(如Preact和Nerv)同名。Preact强调的是小体积(只有4KB),Nerv兼容包括IE8在内的浏览器。你认为Brahmos与React的主要区别是什么?是什么驱使你开发Brahmos?

Yadav:提高应用性能是Brahmos背后的关键动力。Brahmos深受lit-html/hyper-html将应用程序划分为静态和动态部分的想法的启发。遍历和处理过程可以在O(动态节点)内完成,而不是在O(节点)内完成,而在React中是后者。渲染模式还提供了很多静态优化的可能性。当我看到一个有关lit-html的演讲时,我对这个想法很感兴趣,因为应用程序的大部分是静态的,只有少数动态部分会发生变化。 我喜欢React和它的声明式API,我想尝试找出一种与React类似的模式。所以,一开始,我考虑了两个选项。 第一个是开发React Renderer。但这是不可能的,因为这个模式的主要问题是React Element和虚拟DOM。React Element不区分静态元素和动态元素,很难将多个静态元素组合为一个元素。 第二个是使用lit-html作为渲染引擎,并使用React API对其进行包装。但是lit-html API和React API不能直接映射。 所以两个选项都不行了。然后我决定用不同的渲染模式来开发一个实现了React API的库。 另外一个动机是想要了解React的内部原理以及如何构建一个成熟的UI库。

InfoQ:让开发人员能够定义构成应用程序的视图,这是前端框架的一个重要方面。现在有两种主要策略并存:一个是遵循有限DSL的模板,一个是使用成熟语言(如JavaScript或TypeScript)开发的渲染函数。React使用的是渲染函数。Vue和Svelte使用在非JavaScript文件中声明的模板。相反,Brahmos利用了JavaScript中最近加入的标记模板字面量,如nanohtmllighterhtmllit-html或微软的fast-element。你能向我们的读者解释一下模板字面量的好处以及Brahmos是如何使用它的吗?

Yadav:除了模板字面量之外,ES6还为我们带来了一个被低估的特性,那就是给模板打标记。模板字面量标记就像是一个函数,它接收一个字符串数组(字面量/静态部分)作为第一个参数,其余的参数是动态表达式。标记函数的一个独特行为是,如果底层文字字符串没有发生变化,则字符串数组的引用也保持不变。 Brahmos将JSX置换为带标记的模板字面量,原生元素成为静态部分,而JSX表达式成为动态表达式部分。这让我们能够区分内容的静态和动态部分,并以不同的方式对它们加以优化。 我们可以将静态部分视为一个虚拟节点,这将显著减少应用程序的虚拟节点数量,还可以更快地遍历动态部分,以便识别出所发生的变化。 由于字符串数组引用对于给定的模板保持不变,所以我们可以缓存字符串解析结果(将字符串转换为HTML模板标记),即使在一个大列表中多次渲染相同的组件,也不必重复进行解析。这以一种重复的方式提高了组件的性能。 与自定义模板相比,模板字面量也很有优势。模板字面量是JavaScript的一个标准特性,可以发挥JavaScript全部的能力来编写动态表达式。自定义模板(如Vue或Svelte所使用的模板)为动态逻辑带来了有限的语法。 在脚本加载时,对于解析器来说,模板字面量也比对象字面量更友好。对象字面量是JSX创建元素转换的结果。另一种用于在加载时防止进行对象字面量解析的类似技术是将它们转换为JSON字符串。

InfoQ: React最近新增了一系列新的API,其中一些还处于试验阶段。其中,并发模式受到了社区的特别关注,因为它所带来的可能性以及并发渲染方面的复杂性。事实上,一些框架已经决定不接受这个特性。你能告诉我们并发渲染是做什么的吗?Brahmos是如何进行并发渲染的?你认为这个特性所带来的而价值能够超过并发所固有的复杂性吗?

Yadav:我觉得社区里有些人对并发模式秉持了错误的态度。并发模式不仅是指在更新视图时提高性能(更新性能),它也包括在创建视图时提高性能(加载性能)。并发模式可以让开发人员控制视图加载的方式和时间以及渲染的顺序,以便提供最佳的用户体验。 并发模式有助于在应用程序在后台运行时保持UI的交互性。现在,由于浏览器只有一个线程,后台和前台的工作在同一个线程中进行,不过UI库(React/Brahmos)会在线程之间进行调度和切换。 我们可以用版本控制来解释这种行为。在典型的git工作流中,当我们实现一个特性时,我们会新建一个分支并在这个分支上进行开发,然后合并到master分支。但在此期间,如果我们有一个优先级的bug需要修复,就会暂时离开特性分支,处理优先级bug,将变更推给master分支,然后继续在特性分支上开发。如果没有git,我们需要先完成特性任务再去修复bug。 与此类似,并非所有针对浏览器的修改都具有相同的渲染优先级。对于用户来说,最重要的是感知体验,而不是一个库/应用程序在处理渲染任务上花费了多少时间。并发模式试图通过使用启发式、更新源(例如用户交互或JavaScript回调)或声明性提示(useTransitionuseDeferredValue)来决定优先级,从而获得最佳体验。它让程序库在给定的时间内完成最重要的工作,以获得更好的用户体验。 我认为这种模式是绝对值得研究的。并不是说我们不能改进应用程序本身的并发性(可能是通过解除约束、管理竞态条件、自己管理优先级),但它给应用程序带来的复杂性是巨大的。React的并发模式试图将复杂性隐藏在库当中,并提供一个声明性API,提示应用程序应该如何渲染。 实现并发模式很难,而且有很多情况需要考虑。在为Brahmos实现并发模式时,当我以为可以搞定时,另一个问题就会冒出来,我不得不重新进行全盘考量。但考虑到它所带来的可能性,在这个模式上投入是有意义的。 目前,Brahmos支持所有即将到来的并发模式。它支持fiber架构、时间切片、转换、数据获取的suspense和suspense列表。 API保持不变,Brahmos解决并发模式问题所采用的架构和方法与React略有不同。例如: Brahmos没有将行为和启发式划分为多种优先级,而是将更新划分为三个类别: 首先,必须同步渲染和提交的更新——不能暂停(比如由事件引起的更新)。 其次,更新可以暂停,但更新的值不能修改(比如由setState引起的更新,不是源自用户交互)。 第三,推迟的更新,可以暂停,可以变旧,可以被延迟(像异步更新、转换内的更新)。如果前台状态发生变化,异步和延迟更新就会变旧。 Brahmos直接在前台的fiber树上进行高优先级的更改,因为它们必须同步刷新。 Brahmos为每一次转换维护一个单独的更新列表,因此它们可以独立运行,一个转换不会阻碍另一个转换。

InfoQ:考虑到与React API的相似之处,将现有的React代码迁移到Brahmos有多容易?你会建议在新项目中使用Brahmos吗?

Yadav: Brahmos的最终目标是让React的迁移像起个别名一样简单。但目前最大的障碍是与子组件相关的React API。Brahmos将所有静态节点合并为一个,子组件看起来和React不一样——尽管我们确实有一个解决办法。但是第三方React组件确实需要这种支持。 由于Brahmos所做的主要优化是将静态和动态部分划分开来,并将JSX转换为带标记的模板字面量,因此我们也在研究如何对第三方模块进行后处理,以便将createElement语法转换为带标记的模板字面量。顺便说一下,Brahmos也支持createElement语法,只是Brahmos将它归为动态的部分。 我们有一些计划,使用Brahmos来优化现有的React应用程序,但还有很长的路要走。

InfoQ:你可以举一些例子说明Brahmos相对于其他框架的优势吗?

Yadav:Brahmos的主要目标是提高服务器和浏览器UI渲染的性能。目前,Brahmos仍处在开发阶段,所以我们还没有任何基准测试结果。下面是Brahmos提升性能的各种方法:

  1. 包大小(缩小+压缩)

Brahmos:~12kb(涵盖了大部分React API,包括即将推出的并发模式)。 Preact+Preact Compact:~9kb(但Preact不支持并发模式) React+React-DOM:38.5kb(如果支持并发模式,它可能会变得更大一些)。 还有其他一些想法可用来缩小Brahmos体积,另外我们也在研究如何在支持React的第三方库的同时实现摇树优化。

  1. 应用程序包的性能
  2. Brahmos的JSX转换输出体积也比React的应用程序代码小。此外,它还可以减少整体加载解析时间,因为标记的模板字面量是一个字符串,不必像对象文字那样在脚本加载时进行解析。
  3. 渲染/更新时间
  4. 在Brahmos中,我们将静态部分组合为一个节点,遍历变成O(动态节点),在React中为O(节点)。因此,在Brahmos中找到应用于DOM的更新所需的遍历时间要少一些。
  5. 服务器端渲染
  6. 由于Brahmos将JSX转换为标记的模板字面量(已经是一个字符串),因此将应用程序渲染为字符串比渲染来自VDOM (React Element)的字符串具有更高的性能。
  7. 服务器端异步渲染(尚未构建)

除了性能改进之外,我们还计划在服务器端引入异步渲染。Brahmos从一开始就考虑了异步渲染,因此这个架构也可以支持服务器端异步渲染。这种渲染方式可以让服务器和客户端的逻辑保持统一,并且可以消除很多用于标识路由和API调用的约定。

InfoQ:反过来说,使用React比使用Brahmos更容易做哪些事情呢?

Yadav:Brahmos只是针对浏览器和服务器渲染,不支持其他目标。由于使用了VDOM, React更容易支持多个目标,而且由于Brahmos中没有VDOM,所以它不能支持不同的渲染器。 另外,对于主要由动态部分组成的应用程序(比如数据可视化),可能不会从Brahmos中获得太多好处。重度使用数据可视化和主要由动态部分组成的应用程序并不适合使用Brahmos。

InfoQ:Brahmos路线图的下一步要做什么?

Yadav:首先,我们的目标是支持React第三方库,然后是服务器端渲染(SSR)。这样Brahmos就可以立即为社区带来好处。我们还计划在SSR中引入异步渲染支持,这将对当前SSR应用的架构产生重大影响。 我们还将寻求对React开发工具的支持。

嘉宾简介

Sudhanshu Yadav是JavaScript和React的粉丝,对框架的内部结构和工作原理有着浓厚的兴趣。Yadav在HackerRank担任前端架构师,开发了BrahmosJS、response -number-format、packagebind和其他一些OSS工具。他还组织了一个会议小组,讨论不同技术栈的内部原理。

原文链接

Brahmos, a New, Small, React-like UI Framework with Concurrent Rendering – Q&A with Sudhanshu Yadav

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/U9MlNuU277q3121lj4ix
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券