Daily Tech Briefing
AI 科技速览

每天 5 分钟内学习 AI。获取最新的人工智能新闻,理解其重要性,并学习如何将其应用于您的工作。

AI 快讯
Hacker News · 2026/8/3 08:29:57
Bonsai: Janestreet's UI Library

Bonsai: Janestreet's UI Library

AI 中文解读
Jane Street这家华尔街顶级量化交易公司,开源了自己的前端界面库Bonsai。它的最吸引人之处在于:用函数式编程语言OCaml写网页界面,既快又稳,还能让前后端用同一套代码和类型,大幅减少出错。 通俗来说,Bonsai像是React的“升级版”,但理念更超前。它把界面拆成一个个纯函数状态机,每个部分独立管理自身状态,只有数据真正变化时才重新渲染,效率极高。这就好比装修房间,传统方式刷一面墙也要重新搬家具,而Bonsai只动需要动的角落。而且因为状态管理被内置,开发复杂交互界面时不用手动层层传递数据,省心不少。 这项技术主要影响的是开发者群体:它能帮企业用更少的人力维护大型Web应用,尤其适合交易、金融这类对实时性和稳定性要求极高的场景。对普通人来说,未来用到的网页应用可能会更流畅、响应更快,故障更少——比如看行情或在线交易时,界面不会再卡顿掉链子。虽然日常用户感受不到背后的技术,但体验升级是实实在在的。
Bonsai Bonsai Bonsai is a UI library for building performant, reactive web applications in OCaml, partly inspired by Elm. It is used to build almost all web applications inside Jane Street, everything from the corporate directory to tools that monitor and interact with our trading systems. A simple Bonsai component with a little interactivity looks like this: module Dice = struct let faces = [ "⚀"; "⚁"; "⚂"; "⚃"; "⚄"; "⚅" ] ;; let component (graph @ local) = (* Components are implemented as purely functional state machines. *) let face, set_face = Bonsai.state (List.hd_exn faces) graph in (* Components are incrementally rendered, only when the relevant parts of the state change. *) let%arr face and set_face in {%html| <div> You rolled a #{face} <button style="" on_click=%{fun _ -> let index = Random.int (List.length faces) in set_face (List.nth_exn faces index)} > Roll the dice </button> </div> |} ;; end Most internal Jane Street web applications are built with Bonsai Components are implemented as purely functional state machines, and are easily composable. Incrementalization inside the framework means that values don’t get recomputed until necessary. This applies to every value, not just the view. Why Bonsai? Other web frameworks tend to lump together state, incrementality, and rendering into a single abstraction, the UI component. By contrast, Bonsai allows you to compose state and incrementality primitives a la carte. The same primitives that prevent re-rendering the entire page during user interaction can also be used to incrementalize an expensive business logic computation on a live-updating dataset. (If you're used to React, imagine if everything used something very similar to hooks, and state was managed outside of the component hierarchy.) Since state is not associated with explicit components, there is an extensive API for managing the lifecycle and scoping of state as users interact with the page. For instance, if you wanted to embed a collection of stateful UI components inside another UI component (in a tabbed interface, say), Bonsai will handle the state management for you instead of requiring that you manually hoist every internal component's state to the app's top-level model. For more examples of how state is composed, see this composition comparison written by the creator of Bonsai. And because Bonsai is written in OCaml, it becomes possible to use the same language and types on both the backend and frontend. It's hard to overstate the impact this has on legibility and keeping a large web app's codebase manageable, especially when you make pervasive use of OCaml's type system to reduce errors. At Jane Street, many internal systems previously only had terminal UIs, and the framework has made it easy to port the existing types and business logic to the web. Bonsai also comes with a powerful templating language, support for component-specific stylesheets, and a system for whole-app automated tests. Expressive tests that save you from manually clicking through your app One of Bonsai’s most powerful features is its ability to let you easily write realistic tests, in which you programatically manipulate UI elements and watch your DOM evolve. In the following example, we’re testing the behavior of a user-selector. Whatever you type in the text box gets appended to a little “hello” message: let%expect_test "shows hello to a specified user" = let handle = Handle.create (Result_spec.vdom Fn.id) hello_textbox in Handle.show handle; [%expect {| <div> <input oninput> </input> <span> hello </span> </div> |}]; Handle.input_text handle ~get_vdom:Fn.id ~selector:"input" ~text:"Bob"; Handle.show_diff handle; [%expect {| <div> <input oninput> </input> - <span> hello </span> + <span> hello Bob </span>
分享
阅读原文