写点什么

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

2020 年 11 月 07 日

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


2020 年 11 月 07 日 08:002056

评论

发布
暂无评论
发现更多内容

学习路线

hasWhere

学习思路

hasWhere

一文了解Zookeeper

Java旅途

kafka zookeeper 分布式

第二周作业

Geek_ac4080

Week_02学习总结

golangboy

第二周作业

zero2onemore

第二周学习总结

熊桂平

极客大学架构师训练营

架构师训练营学习小结(2020.9.14 - 9.20)

zjzj2017

数字货币合约交易所开发源码,永续合约开发app

WX13823153201

数字货币合约交易所开

第2周

paul

框架设计作业

ABS

架构师训练营学习小结(第二周2020.9.21 - 9.27)

zjzj2017

收款神器!解读聚合收款码背后的原理

楼下小黑哥

最通俗易懂的——如何将机器学习模型的准确性从80%提高到90%以上

计算机与AI

学习 数据科学

TensorFlow 篇 | TensorFlow 2.x 基于 HParams 的超参数调优

Alex

tensorflow keras hparams tensorboard 超参数调优

基于数组的有界阻塞队列 —— ArrayBlockingQueue

程序员小航

Java 源码 队列 源码阅读 JUC

Redis 缓存性能实践及总结

vivo互联网技术

redis redis集群 redis监控

ARTS Week11

丽子

架构师训练营第二周作业

zjzj2017

栈与队列简介

Java旅途

数据结构 队列

《我在你床下》观后感

徐说科技

架构师第 2 课作业及学习总结

小诗

极客大学架构师训练营

逼着面试官问了我ArrayList和LinkedList的区别,他对我彻底服了

沉默王二

Java ArrayList linkedlist

架构师训练营营第 1 期之框架设计02

天行健

高难度对话读书笔记—求助的勇气

wo是一棵草

架构第二周总结

Geek_Gu

极客大学架构师训练营

c++ 杂谈3

菜鸟小sailor 🐕

第二周作业一

dll

【架构师训练营 1 期】第二周作业

诺乐

【架构师训练营1期】第二周学习总结

诺乐

java安全编码指南之:敏感类的拷贝

程序那些事

Java java安全编码 java安全 java安全编码指南

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