top of page

Jane Street 的 Bonsai 登上 Hacker News,但它真正的对手是 React 的组件模型

8月31日
讀畢需時 15 分鐘

Jane Street 的 Bonsai 于 8 月登上 Hacker News 首页,获得数百张投票,并引发了一场更深入的讨论:复杂界面应如何管理变化。这一开源 OCaml 库并非只是提供了另一种渲染按钮和表单的方式。它挑战的是塑造了主流前端开发的组件模型。

这场讨论之所以重要,是因为 Bonsai 源自一个要求异常严苛的生产环境。Jane Street 表示,几乎所有内部 Web 应用都使用该库。这些应用涵盖企业通讯录,以及用于监控和操作交易系统的工具。

因此,Bonsai 的主要对手并非某一个竞争库,而是这样一种假设:在 React 及其后继者的强化下,状态、渲染和增量更新都应归属于 UI 组件层级。Jane Street 将这些关注点分离开来,并把增量计算应用到可见页面之外。

这一设计吸引了重视类型化函数式编程和可预测状态机的开发者。它也暴露出一个艰难的采用问题:一个围绕 OCaml、Js_of_ocaml 和 Jane Street 内部需求构建的框架,面对的人才和软件包市场远比 JavaScript 或 TypeScript 狭窄。

Hacker News 的关注让这种取舍变得更加清晰。Bonsai 为大型、持续更新的应用提供了一种异常连贯的答案,但一家公司的内部连贯性并不保证它能在更广泛的 Web 生态中顺利迁移。

Hacker News 的关注实际改变了什么

新闻并不是 Bonsai 突然发布,而是一个成熟的内部框架走出了原本的 OCaml 受众圈。

Jane Street 于 2019 年 3 月下旬开始开发 Bonsai。其历史文档称,该项目源于开发者看到学生在使用早期 Jane Street 框架 Incr_dom 时遇到困难。随着应用不断扩大,组合应用变得困难;而连接较小组件又带来了新的错误来源。

因此,Bonsai 已存在多年。8 月的 Hacker News 讨论改变的是它的可见度,而非技术基础。截至 8 月 31 日,讨论页面显示 390 分和 154 条评论,远高于最初记录该报道时的 82 分和 24 条评论。

这种增长之所以重要,是因为评论并不只聚焦于 OCaml 语法。开发者讨论了增量计算、前后端共享类型、JavaScript 互操作性、WebAssembly、测试,以及离开主导生态系统的成本。

Bonsai repository 也呈现出比典型实验性框架更持续的开发迹象。截至 8 月底,GitHub 显示其约有 1,400 个星标、57 个分叉、148 次提交、7 个开放议题和 2 个开放拉取请求。

这些数字并不能证明它已被广泛用于生产环境。星标衡量的是关注度,而较低的议题数量既可能反映稳定性,也可能意味着外部社区相对较小。更有价值的信号是 Jane Street 将 Bonsai 描述为标准内部基础设施。

该公司表示,几乎所有 Web 应用都使用 Bonsai。这一说法让该库与周末开发的框架或演示项目划入不同类别。Jane Street 在职责和风险等级截然不同的界面中都依赖它。

该仓库描述了一类用于监控和操作交易系统的应用。这类界面处理不断变化的数据、协调用户操作,并呈现准确性至关重要的状态。它们类似于金融、物流、基础设施和企业管理中常见的密集型运营软件。

这一背景解释了 Hacker News 的反应。当普通页面渲染只是问题的一部分时,Bonsai 提供了一个窗口,让人看到一家工程驱动型交易公司如何看待前端架构。

该讨论串还揭示了一个重要误解。起初,一些评论者将 Bonsai 视为 React 的 OCaml 替代方案。其他参与者则指出,其核心抽象更具普适性:一种可增量更新、可组合的状态机,能够产出 Web 界面、终端界面或其他结果。

这一区别构成了本文的核心张力。React 从组件这一用户界面的组织单元出发。Bonsai 则从计算和状态机出发,再让浏览器渲染器消费其结果。

在应用包含实时市场数据、筛选表格、权限、异步请求和派生计算之前,这种差异听起来或许只是理论问题。到了那时,控制哪些内容需要重新计算,可能与控制哪些内容需要重新渲染同样重要。

这篇 Hacker News 帖子并未让 Bonsai 成为主流框架。它向更广泛的开发者群体展示了一个具体案例:一种替代性架构选择已经在要求严苛的组织内部经受了考验。

为什么 Jane Street 的 UI 库给组件模型带来压力

Bonsai 通过将增量工作视为全应用属性,而非渲染优化,给以组件为中心的框架带来压力。

大多数当代前端开发者以组件为思考单位。组件拥有或接收状态,计算视图,并参与一棵树。随后,框架通过记忆化、细粒度响应式、虚拟 DOM 比较、编译器或调度机制来避免不必要的工作。

Bonsai 将这一组合拆分开来。它的状态和增量计算原语可独立于渲染视图进行组合。同一套避免刷新无关界面元素的系统,也可以避免重复执行昂贵的业务计算。

增量计算指的是:仅重新计算受变化输入影响的部分,从而更新结果。Jane Street 开发了独立的 Incremental library,用于构建可自动追踪依赖关系的计算。

Bonsai 将这一理念应用于整个应用图。即使某些值并不直接表示 HTML,只要其依赖关系未发生变化,它们就保持静止。渲染只是更广泛计算模型的一个消费者。

React 的职责早已超出最初的视图库,但其概念中心仍是组件树。将状态与组件放置在一起,为构建应用提供了一种易于理解的方式。但这也让开发者必须在该层级中处理身份、生命周期、依赖数组、闭包和数据流动问题。

Bonsai 则在显式组件层级之外管理状态。其文档要求 React 用户想象这样一种应用:几乎所有内容都类似 hooks,但状态存在于组件树之外。

这种方式改变了开发者处理一个组件内一组有状态控件的方式。标签页界面就是一个简单例子。每个标签页都可能包含自己的控件、本地选择和异步活动。

在以组件为中心的系统中,开发者通常通过让组件保持挂载、将状态上提、分配稳定键或添加独立存储来保留状态。每种选择都会改变生命周期行为,并可能引入意外重置或过期值。

Jane Street 表示,Bonsai 提供了生命周期和状态作用域 API,无需将每个嵌套组件的状态都手动上提到顶层模型。这并不等于消除了复杂性,而是将复杂性转移到一个语义更明确的框架中。

这一设计尤其适合运营型软件。一个交易仪表盘可能展示选中的账户、多条实时数据流、计算出的敞口、待处理操作和筛选后的历史记录。一个输入可以影响系统中的多个部分,却未必天然属于某一个视觉组件。

Bonsai 将这些关系建模为依赖图。当输入发生变化时,该图决定哪些计算需要获得新值。可见页面依然重要,但它不再定义架构本身。

这一模型还支持非浏览器目标。核心 Bonsai 库构建增量式、可组合的状态机。Bonsai_web 将这些原语专用于浏览器界面,而 Bonsai_term 则将其用于交互式终端应用。

这种拆分强化了 Jane Street 的论点。如果同一套状态和计算模型能够同时驱动 Web 和终端界面,那么视觉组件就不可能是最基础的抽象。

React 并非停滞不前,其生态中也包含状态机、signals、查询缓存、可观察存储和细粒度响应式库。开发者可以用多个工具组合出类似行为。

Bonsai 的挑战在于整合。Jane Street 为状态、依赖、效果、渲染和测试提供了一套统一的类型化模型。主流 JavaScript 团队则常常组合使用具有不同假设和生命周期规则的库。

这种压力是概念层面的,而非商业层面的。React 不太可能因为一个 OCaml 库而失去显著市场份额。然而,Bonsai 证明了组件层级是一种设计选择,而非交互式软件不可避免的属性。

真正的机制是无处不在的增量计算

Bonsai 的决定性机制,是它能够在界面代码和业务逻辑中追踪变化。

一个基础 Bonsai 组件被实现为纯函数式状态机。状态机描述操作如何将当前状态转换为下一个状态,而不修改隐藏值。这种结构让行为更容易检查和测试。

随后,该库会对围绕这些状态机构建的计算进行增量求值。若某个值发生变化,Bonsai 只更新依赖该值的下游工作。无关计算会保留现有结果。

前端框架通常提供这种行为的较窄版本。React 可以通过记忆化跳过部分渲染,其他框架则会在 signal 或属性层面追踪依赖。Bonsai 的主张是,增量化适用于其计算图中的每一个值。

设想一张包含来自多个交易系统持仓的实时表格。用户可能更改一个筛选条件、更新选中的账户,或接收服务器发来的新值。这些输入会影响不同行、总计、控件和警告的不同子集。

以组件为导向的应用可以处理这类工作负载。开发者可能会使用选择器、记忆化计算、规范化存储、虚拟化和查询缓存。困难在于,随着应用演进,如何维护这些连接关系。

Bonsai 将依赖图作为框架的核心抽象。计算由其关系已为增量引擎所知的值组合而成。因此,系统可以决定哪些节点需要重新求值。

这一机制反映了 Jane Street 使用 Incr_dom 的历史。根据该项目的 design history,早期框架能够隔离组件,但难以自动组合它们。

其中一个问题涉及合并独立组件的视图。另一个问题涉及能够更新共享模型的可见性回调。Incr_dom 将协调这些更新的责任交给了应用开发者。

Bonsai 的设计者通过移除妨碍组合的假设作出了回应。其核心结果变得具有通用性,而不再局限于虚拟 DOM 节点。动作和模型随后成为实现细节,而非组件之间共享的公开类型参数。

这些变化并非表面上的 API 清理。它们缩小了相邻组件干扰彼此内部状态的途径。组件可以通过输入和结果进行通信,而不是跨越边界去操控另一个组件的模型。

这一设计还受益于 OCaml 的类型系统。由于 Js_of_ocaml 会将 OCaml 编译为 JavaScript,Jane Street 可以在服务器和浏览器中使用同一种语言,以及大量相同的类型。

共享类型减少了各层之间的转换。应用可以一致地表示业务数据,而不必为后端处理定义一种模型、再为 TypeScript 客户端定义另一种模型。

当一家公司同时掌控技术栈两端时,这一优势会更加明显。Jane Street 控制其后端服务、内部用户界面、库和部署环境,因此可以在整条链路中标准化使用 OCaml。

当 Bonsai 应用接入更广泛的 Web 生态系统时,取舍便会显现。浏览器 API 和第三方 JavaScript 包仍然需要兼容的绑定。一个流行的 JavaScript 库通常会提供 TypeScript 声明、示例和集成指南,而 OCaml 团队无法直接使用这些资源。

Hacker News 上的讨论不断回到这个问题。评论者提到了 Fable、ClojureScript、Scala.js、Kotlin/JS、Google Web Toolkit,以及其他试图将非 JavaScript 语言带入浏览器的项目。

这些项目表明,共享语言开发并不是一个新目标。它们也说明,技术上的一致性很少能决定采用与否。互操作性、招聘、文档、调试工具和库覆盖度,往往决定一种语言能否在其核心社区之外延续。

Bonsai 的机制仍然值得关注,因为它的设计并不只是为了避开 JavaScript。Jane Street 构建它,是为了解决一个规模可观的 OCaml 应用环境中已经存在的组合与增量更新问题。

这种源于生产环境的背景赋予了该架构可信度。但这并不能证明其他组织面临相同约束,或应当接受同样的生态系统成本。

测试是 Bonsai 最有力的实践论据

当其状态机设计能够将复杂的界面行为转化为确定性测试时,Bonsai 最具说服力。

Jane Street 将自动化测试视为核心功能,而非外部附加组件。开发者可以创建一个组件、检查其虚拟 DOM、模拟一个动作,并将产生的变化与预期输出进行比较。

Expect 测试会将预期结果存放在测试代码旁。当行为发生变化时,测试运行器会展示先前输出与当前输出之间聚焦的差异。

对于一个文本输入框,测试可以先记录空的问候语,然后模拟输入姓名,并只展示虚拟 DOM 中变化的文本。开发者无需启动浏览器,也无需手动点击操作界面。

Bonsai 测试还可以模拟服务器调用,并检查视图背后状态的变化。这一点很重要,因为许多界面故障并非纯粹的视觉问题。它们可能涉及错误的状态转换、过期的派生数据,或将响应应用到了错误状态。

确定性的状态机让这些路径更容易复现。测试可以在每次运行时提供相同的初始模型、动作序列和模拟的外部响应。

这种方法符合 Jane Street 的工程文化。金融工具需要可审查的行为,尤其是在一个视觉控件可能触发运营操作时。截图可以揭示布局变化,但无法完整解释底层状态为何发生迁移。

基于浏览器的端到端测试仍然有其作用。它们能够捕捉虚拟 DOM 测试无法保证的 CSS、焦点、无障碍访问、浏览器兼容性和集成故障。Jane Street 并未证明 Bonsai 测试能够消除这一需求。

其优势在于浏览器之下存在更大范围的可测试层。开发者可以验证组件行为、状态转换和生成的标记,而无需为每种情况都承担完整浏览器会话的设置与运行成本。

React 团队也可以构建类似的测试工作流。React Testing Library 鼓励以用户可见行为驱动测试,而 Playwright 和 Cypress 负责浏览器自动化。状态机库可以让状态转换更加明确。

Bonsai 的不同之处在于,其可测试性源自架构本身。纯状态机和增量值已经暴露了测试所需的输入与输出。测试系统不必从分散的 hooks 和可变服务中重建执行顺序。

这种集成可以减少代码审查过程中的歧义。expect 块中的差异展示了一个动作如何改变生成的 DOM。审查者可以将这一变化与引发它的代码一并检查。

维护成本仍然存在。大型文本快照可能变得嘈杂,开发者有时会在不理解变化的情况下予以批准。专注于不稳定实现细节的测试,也可能带来工作量,却无法捕捉有意义的回归。

Bonsai 通过有针对性的差异缓解了部分风险,但无法消除糟糕的测试设计。团队仍必须选择真正重要的行为,并避免将每一次标记变化都视为失败。

文档带来了另一项顾虑。一位 Hacker News 评论者称公开文档较为稀疏,并表示源代码更能揭示该框架的设计。这一观察具有主观性,但它指出了一个实际的采用障碍。

Jane Street 提供快速入门、概念指南、示例、API 材料、历史说明,以及其 Signals and Threads 播客中的一场框架讨论。外部团队仍缺少公司内部可获得的深厚组织知识。

小众框架需要异常出色的公开文档,因为用户无法依赖大量教程或具备相关经验的同事。评估 Bonsai 的团队需要一份可搜索的架构决策、示例和本地惯例记录。

这一需求并不止于 Bonsai。工程知识库可以帮助团队将框架文档与内部决策和可运行示例联系起来。但它无法取代健康的外部社区。

因此,测试或许是 Bonsai 最具可迁移性的经验。即使从不采用 OCaml 的团队,也可以研究显式状态机和增量依赖如何让复杂界面行为更易于验证。

Hacker News 的争论无法证明什么

Hacker News 的兴趣证明该架构值得讨论,而不是证明 Bonsai 应成为外部团队的默认选择。

Bonsai 最有力的证据来自 Jane Street 自身。该公司称,它几乎在所有内部 Web 应用中使用该框架,其中包括与交易运营相关的软件。

这是有意义的生产经验。但它同样是一个由罕见条件塑造的单一组织案例研究。Jane Street 拥有许多 OCaml 开发者,维护重要的库,掌控其后端技术栈,并能在长期内资助内部工具建设。

大多数公司则从相反的位置起步。其前端开发者熟悉 JavaScript 或 TypeScript,设计系统面向 React、Vue、Angular 或 Web Components,监控、无障碍访问、测试和招聘实践也都以这些生态系统为前提。

采用 Bonsai 不只是学习一个新 API。团队还需要 OCaml 专业能力、Js_of_ocaml 构建流水线、必要浏览器库的绑定,以及对较小公开生态系统的运营信心。

该仓库的 1,400 个 stars 显示出好奇心,但与主流前端社区相比仍然很小。57 个 forks 表明存在一些外部试验,但公开仓库活动并不能揭示有多少组织在运行生产级 Bonsai 应用。

Jane Street 并未公布客户名单,因为 Bonsai 并未被定位为商业平台。因此,外部采用情况难以衡量。包下载量、独立案例研究、会议演讲和长期维护的第三方项目将提供更有力的证据。

该库较少的未解决 issue 数量同样含义不明。七个未解决 issue 可能意味着维护谨慎,也可能意味着许多内部问题通过公众无法看到的系统报告和解决。

公开贡献者还会遇到另一种不对称。Jane Street 的工程师可以通过内部应用、同事和设计历史理解该框架;外部开发者则必须从公开文档和源代码中推断更多信息。

互操作性是最大的技术不确定性。Bonsai 通过 JavaScript 生成浏览器界面,但浏览器周边生态仍以 JavaScript 为第一语言。每一个不受支持的依赖项都会带来决策:编写绑定、替换包,或在内部构建功能。

Jane Street 可以接受这一成本,因为它已经对以 OCaml 为中心的技术栈投入颇多。一家较小的公司在集成上花费的工程时间,可能超过通过更优增量语义节省的时间。

与 React 的比较同样需要克制。React 的组件模型存在众所周知的复杂性,但其生态系统提供了广泛的库、设计系统、教育材料、调试工具和经验丰富的开发者。

Bonsai 无需击败 React 才能发挥价值。它可以很好地服务于 Jane Street,同时在其他地方仍是一种专业化选择。架构论据和采用论据应当分开评估。

8 月的讨论串还包含了因厌倦 JavaScript 而产生的热情。一些评论者开玩笑说,他们愿意学习 OCaml,以避免编写 JavaScript。这种情绪可以推动试验,但对某种语言的反感并不能证明另一框架适合生产环境。

其他评论者提到了 ClojureScript、Scala.js、Fable、Kotlin/JS、Phoenix LiveView,以及 Google Web Toolkit 等较早的系统。这些例子削弱了“前后端共享类型是 Bonsai 独有优势”的说法。

它们强化了另一项结论。许多生态系统都曾追求强类型、非 JavaScript 的浏览器开发,但 JavaScript 和 TypeScript 仍占主导地位。反复出现的障碍在于完整的开发环境,而非能否为浏览器编译代码。

Bonsai 的公开证据支持一项谨慎的说法:Jane Street 围绕真实的内部需求构建了一个连贯的框架。它并不能证明,对于典型外部组织而言,它具有更低成本、更佳性能或更高可靠性。

将定义 Bonsai 下一阶段的三个信号

Bonsai 更广泛的相关性如今取决于独立采用、公开生态系统深度,以及其增量模型能带来可衡量运营收益的证据。

第一个信号是来自 Jane Street 之外的重大生产案例研究。一个可信的案例应描述应用规模、数据流、团队构成、集成需求和维护经验。

一次独立部署并不能证明其具有广泛适用性。但它仍会检验:在没有 Jane Street 的 OCaml 文化和内部支持网络的情况下,Bonsai 的优势是否依然成立。

一项案例研究若能表明,小型团队可以学会该框架、集成所需的浏览器库并维护复杂应用,将增强其可移植性的论据。反之,若有报告称绑定工作长期拖延或招聘困难,则会削弱这一论据。

第二个信号是公开工具和文档的增长。关键指标不应只有 GitHub stars。开发者还应关注是否出现更多持续维护的示例、可复用组件、编辑器支持、集成指南、独立教程以及外部贡献者。

文档还必须解释故障模式。团队需要获得关于如何分析增量图、跟踪状态生命周期、诊断意外重算、处理异步副作用以及集成 JavaScript 包的指导。

更丰富的公开生态系统将缩小 Jane Street 员工与外部用户之间的知识差距。若大多数答案仍需通过阅读框架内部实现才能获得,Bonsai 在常规项目期限内仍会难以评估。

第三个信号是对比性的技术证据。Jane Street 解释了为何增量计算适合其应用,但公开基准测试和详细工程报告能让这项权衡更容易评估。

有价值的证据应在真实应用中衡量更新延迟、重复计算、内存使用、包体积、测试执行和代码复杂度。一个小型反例演示或合成基准测试几乎说明不了什么。

最有价值的对比,应考察使用 Bonsai 与设计完善的主流技术栈分别实现的高密度、实时更新应用。它应披露每个版本在哪些地方使用了记忆化、缓存、虚拟化或外部状态管理。

如果 Bonsai 在保持可预测延迟的同时,需要人工维护的优化边界更少,其架构论证就会更有力。若当代 TypeScript 技术栈能以更易招聘和集成的方式取得相近结果,Bonsai 的吸引力仍将局限于特定场景。

开发者不必等到出现胜者才从中汲取经验。Bonsai 已经表明,状态、增量性与渲染不必共用同一个抽象。

它也展示了一家公司如何将全域语言一致性转化为前端优势。当组织同时掌控服务器与浏览器两端环境时,相同的类型和业务逻辑便可在两者之间流转。

Hacker News 带来的最终启示,与其说是选择框架,不如说是提出更好的架构问题。组件树描述的是应用真实的依赖关系,还是仅仅描述其视觉布局?每次状态变化后,哪些计算会重复执行?测试能否在没有浏览器的情况下复现有意义的行为?

开发普通内容网站的团队,可能很难从 Bonsai 的模型中获得多少收益。构建实时运营界面、分析工作台或深度状态化工具的团队,则更有理由研究它。

据该公司称,Bonsai 已经通过了 Jane Street 的内部生产环境考验。它的下一项考验,是外部开发者能否在不承担不成比例生态系统成本的前提下,获得同样的清晰度。

请带着这一差异关注该仓库、独立项目以及未来的 Hacker News 讨论。重要的问题不是 Bonsai 是否会取代 React,而是它的增量状态机模型能否走出设计它的组织。

 
 

免费开始使用

一款本地优先的AI助手

为了获得更好的人工智能体验,

remio 目前仅支持Windows 10+ (x64)M-Chip Mac

你的 AI 工作伙伴

remio 一起高效工作

规划、创作、交付

一站式完成

bottom of page