#远程 #RPC #对象 #JavaScript #API 绑定

wsdom

无需往返的 Rust 到 JavaScript *远程方法调用* 或 *分布式对象* 系统

1 个不稳定版本

0.0.1 2024年3月15日

#235 in #远程

MPL-2.0 许可证

1.5MB
17K SLoC

TypeScript 16K SLoC // 0.5% comments Rust 1K SLoC // 0.0% comments

"LiveView" 框架,如 Phoenix LiveViewLiveViewJSDioxus LiveView 都很棒。但我们为什么要限制自己使用 LiveView 呢?为什么不尝试 LiveEverything 呢?

WSDOM

crates.io crates.io

WSDOM 是一个无需往返的 Rust → JavaScript 远程方法调用分布式对象 系统。它允许 Rust 代码持有 JavaScript 对象,并通过网络调用方法/函数。

WSDOM 可以用来添加网络依赖的功能到网页,而无需编写 JS 代码或创建 API 端点。它还可以集成到 "LiveView" 风格的 Rust 网页框架中,以暴露对完整 Web API 的访问。

快速示例

以下是一个使用 WSDOM 将 <div>Hello World!</div> 放置在网页上的示例。

// this Rust code runs on your web server
fn hello(browser: wsdom::Browser) {
    let document = wsdom::dom::document(&browser) // get hold of a Document object
    let body = document.get_body(); // get the <body /> of that document object
    let elem = document.create_element(&"div", &wsdom::undefined()); // create a <div />
    elem.set_inner_text(&"Hello World!"); // set the text
    body.append_child(&elem); // add the <div /> to the <body />
}
// this JavaScript code runs on the browser
WSDOMConnectWebSocket("ws://my-website.domain:4000/");

我们的完整 "Hello World!" 代码可以在 这里 找到。

关键特性(和反特性)

  • WSDOM 根据 .d.ts TypeScript 定义生成基于 强类型 的 Rust 存根。
  • 使用 WSDOM 调用 JS 代码是 无需往返 的。这段 Rust 代码
    let mut val: JsNumber = browser.new_value(&1.0);
    for _ in 0..100 {
        val = wsdom::js::Math::cos(&browser, &val); // Math.cos on the JS side
    }
    
    根本不会在网络上阻塞;它将在微秒内完成。
    • 无需往返调用之所以可能,是因为 WSDOM 在 JS 端保持值,仅在明确请求时才将它们发送回 Rust。要获取上面循环计算出的值,可以这样操作
      let val_retrieved: f64 = val.retrieve_float().await;
      println!("the value of (cos^[100])(1.0) computed in JavaScript is {val_retrieved}");
      
      .await 将需要一次网络往返。
  • 由于无需往返的设计,WSDOM 基本上 无法处理 JS 异常
    • 如果我们上面的循环中的 Math.cos 调用抛出异常,Rust 循环仍将完成所有 100 次迭代,而不会崩溃或发出任何警告(有关原因,请参阅 How It Works)。正如你所预期的那样,这意味着使用 WSDOM 的代码 非常难以调试
  • WSDOM 是 单向的。Rust 代码可以调用 JS 代码,但不能反过来。
    • 为了使事件处理成为可能,我们提供了基于 Future 的交互性;我们将 JS 回调连接到可以在 Rust 端等待的流。
      async fn example(browser: Browser, button: &HTMLElement) {
          let (stream, callback) = wsdom::callback::new_callback::<MouseEvent>(&browser);
          button.add_event_listener(&"click", &callback, &wsdom::undefined());
          let _click_event: MouseEvent = stream.next().await; // wait for the Stream to yield
          println!("button was clicked on the browser!");
      }
      
  • WSDOM 是 传输无关框架无关执行器无关 的。话虽如此,我们提供了一个集成库,方便您在 Axum 网页框架(使用 Tokio 执行器)上使用 WebSocket 快速入门。

示例

托管示例可供使用。查看它们时,我建议您打开浏览器中的网络开发工具来查看 WebSocket 流量。

  • 计数器示例:[代码](https://github.com/examples/many-examples/src/counter.rs) [演示](http://141.145.215.129:4000/counter)
  • 音频 API 示例:[代码](https://github.com/examples/many-examples/src/audio.rs) [演示](http://141.145.215.129:4000/audio)
  • 画布 API 示例:[代码](https://github.com/examples/many-examples/src/canvas.rs) [演示](http://141.145.215.129:4000/canvas)

比较

web-sys

WSDOM 与 web-sys 扮演着类似的角色(以及一点 js-sys),但不同之处在于,我们不是在同一个浏览器中运行您的 Rust 代码作为 WebAssembly,而是让您通过 WebSocket 连接运行您的 Rust 代码。

WSDOM 将 JS API 转换为 Rust 的方式与 web-sys 不同。我们是从 TypeScript 声明中转换的,而不是直接从 WebIDL 中转换。网络差距还意味着我们的可选类型以 JsNullable<_>(与 web-sys 的 Option<_> 相比)的形式出现。

jsdom

WSDOM 和 jsdom 类似,我们都在网页浏览器外部公开了网页浏览器的 API。jsdom 是通过自己实现 API 来实现的。WSDOM 是通过转发调用到在 WebSocket 连接上运行的真正网页浏览器来实现的。

详情

工作原理》文档更详细地描述了 WSDOM 的工作方式。

该软件包位于 crates.io,文档位于 docs.rs

免责声明

请自行承担使用 WSDOM 的风险。它最多是 alpha 质量。

我们的 .d.ts 加载器生成的 Rust 代码可能在 WSDOM 版本之间发生变化。

依赖关系

~1.5–2.3MB
~49K SLoC