Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

与 JavaScript 集成:wasm-bindgen、web_sys 和 HtmlElement

Integrating with JavaScript: wasm-bindgen, web_sys and HtmlElement

Leptos 提供了各种工具,让你无需离开框架的世界就能构建声明式 Web 应用程序。诸如响应式系统、componentview 宏以及路由(router)等功能,让你无需直接与浏览器提供的 Web API 交互就能构建用户界面。而且它们让你直接在 Rust 中完成这一切,这非常棒——假设你喜欢 Rust。(既然你已经读到了本书的这一部分,我们假设你是喜欢 Rust 的。)

Leptos provides a variety of tools to allow you to build declarative web applications without leaving the world of the framework. Things like the reactive system, component and view macros, and router allow you to build user interfaces without directly interacting with the Web APIs provided by the browser. And they let you do it all directly in Rust, which is great—assuming you like Rust. (And if you’ve gotten this far in the book, we assume you like Rust.)

生态系统中的 crate,例如 leptos-use 提供的极佳实用工具集,可以通过为许多 Web API 提供 Leptos 特有的响应式封装,带你走得更远。

Ecosystem crates like the fantastic set of utilities provided by leptos-use can take you even further, by providing Leptos-specific reactive wrappers around many Web APIs.

尽管如此,在许多情况下,你仍需要直接访问 JavaScript 库或 Web API。本章可以提供帮助。

Nevertheless, in many cases you will need to access JavaScript libraries or Web APIs directly. This chapter can help.

使用 wasm-bindgen 调用 JS 库

Using JS Libraries with wasm-bindgen

你的 Rust 代码可以编译为 WebAssembly (WASM) 模块并加载到浏览器中运行。然而,WASM 无法直接访问浏览器 API。相反,Rust/WASM 生态系统依赖于从你的 Rust 代码生成绑定(bindings)到托管它的 JavaScript 浏览器环境。

Your Rust code can be compiled to a WebAssembly (WASM) module and loaded to run in the browser. However, WASM does not have direct access to browser APIs. Instead, the Rust/WASM ecosystem depends on generating bindings from your Rust code to the JavaScript browser environment that hosts it.

wasm-bindgen crate 是该生态系统的核心。它提供了一个接口,用于使用注解标记 Rust 代码的部分,告诉它如何调用 JS,并提供了一个 CLI 工具用于生成必要的 JS 粘合代码。你其实一直在不知不觉中使用它:trunkcargo-leptos 底层都依赖于 wasm-bindgen

The wasm-bindgen crate is at the center of that ecosystem. It provides both an interface for marking parts of Rust code with annotations telling it how to call JS, and a CLI tool for generating the necessary JS glue code. You’ve been using this without knowing it all along: both trunk and cargo-leptos rely on wasm-bindgen under the hood.

如果你想从 Rust 调用某个 JavaScript 库,你应该参考 wasm-bindgen 文档中关于从 JS 导入函数的部分。将单个函数、类或值从 JavaScript 导入到你的 Rust 应用程序中相对容易。

If there is a JavaScript library that you want to call from Rust, you should refer to the wasm-bindgen docs on importing functions from JS. It is relatively easy to import individual functions, classes, or values from JavaScript to use in your Rust app.

直接将 JS 库集成到应用中并不总是那么容易。特别是,任何依赖于特定 JS 框架(如 React)的库可能都难以集成。以某种方式操作 DOM 状态的库(例如富文本编辑器)也应谨慎使用:Leptos 和 JS 库可能都会假设它们是应用状态的最终事实来源,因此你应该小心划分它们的职责。

It is not always easy to integrate JS libraries into your app directly. In particular, any library that depends on a particular JS framework like React may be hard to integrate. Libraries that manipulate DOM state in some way (for example, rich text editors) should also be used with care: both Leptos and the JS library will probably assume that they are the ultimate source of truth for the app’s state, so you should be careful to separate their responsibilities.

使用 web-sys 访问 Web API

Accessing Web APIs with web-sys

如果你只需要访问一些浏览器 API,而不需要引入单独的 JS 库,你可以使用 web_sys crate。它为浏览器提供的所有 Web API 提供了绑定,将浏览器类型和函数 1:1 地映射到 Rust 结构体和方法。

If you just need to access some browser APIs without pulling in a separate JS library, you can do so using the web_sys crate. This provides bindings for all of the Web APIs provided by the browser, with 1:1 mappings from browser types and functions to Rust structs and methods.

总的来说,如果你在问 “如何用 Leptos 做到某事?”,而该事涉及访问某些 Web API,那么寻找一个纯 JavaScript 的解决方案并使用 web-sys 文档将其翻译成 Rust 是一个不错的方法。

In general, if you’re asking “how do I do X with Leptos?” where do X is accessing some Web API, looking up a vanilla JavaScript solution and translating it to Rust using the web-sys docs is a good approach.

在本节之后,你可能会发现 wasm-bindgen 指南中关于 web-sys 的章节对深入阅读很有帮助。

After this section, you might find the wasm-bindgen guide chapter on web-sys useful for additional reading.

启用特性 (Features)

Enabling features

为了保持较低的编译时间,web_sys 高度依赖特性门控(feature-gated)。如果你想使用它众多的 API 之一,你可能需要启用某个特性。

web_sys is heavily feature-gated to keep compile times low. If you would like to use one of its many APIs, you may need to enable a feature to use it.

使用某个条目所需的特性总是列在其文档中。例如,要使用 Element::get_bounding_rect_client,你需要启用 DomRectElement 特性。

The features required to use an item are always listed in its documentation. For example, to use Element::get_bounding_rect_client, you need to enable the DomRect and Element features.

Leptos 已经启用了一大堆特性——如果所需的特性已经在此启用,你就不必在自己的应用中启用了。否则,将其添加到你的 Cargo.toml 中即可!

Leptos already enables a whole bunch of features - if the required feature is already enabled here, you won't have to enable it in your own app. Otherwise, add it to your Cargo.toml and you’re good to go!

[dependencies.web-sys]
version = "0.3"
features = ["DomRect"]

然而,随着 JavaScript 标准的演进和 API 的编写,你可能希望使用一些技术上尚未完全稳定的浏览器功能,例如 WebGPUweb_sys 会紧跟(可能频繁变动的)标准,这意味着不提供稳定性保证。

However, as the JavaScript standard evolves and APIs are being written, you may want to use browser features that are technically not fully stable yet, such as WebGPU. web_sys will follow the (potentially frequently changing) standard, which means that no stability guarantees are made.

为了使用这些 API,你需要添加 RUSTFLAGS=--cfg=web_sys_unstable_apis 作为环境变量。这可以通过将其添加到每个命令中,或者添加到仓库的 .cargo/config.toml 中来完成。

In order to use this, you need to add RUSTFLAGS=--cfg=web_sys_unstable_apis as an environment variable. This can either be done by adding it to every command, or add it to .cargo/config.toml in your repository.

作为命令的一部分:

As part of a command:

RUSTFLAGS=--cfg=web_sys_unstable_apis cargo # ...

.cargo/config.toml 中:

In .cargo/config.toml:

[env]
RUSTFLAGS = "--cfg=web_sys_unstable_apis"

在视图中访问原始 HtmlElement

Accessing raw HtmlElements from your view

框架的声明式风格意味着你无需直接操作 DOM 节点来构建用户界面。但在某些情况下,你希望直接访问代表视图一部分的底层 DOM 元素。本书关于“非受控输入”的章节展示了如何使用 NodeRef 类型来实现这一点。

The declarative style of the framework means that you don’t need to directly manipulate DOM nodes to build up your user interface. However, in some cases you want direct access to the underlying DOM element that represents part of your view. The section of the book on “uncontrolled inputs” showed how to do this using the NodeRef type.

NodeRef::get 返回一个类型正确的 web-sys 元素,可以直接进行操作。

NodeRef::get returns a correctly-typed web-sys element that can be directly manipulated.

例如,考虑以下代码:

For example, consider the following:

#[component]
pub fn App() -> impl IntoView {
    let node_ref = NodeRef::<Input>::new();

    Effect::new(move |_| {
        if let Some(node) = node_ref.get() {
            leptos::logging::log!("value = {}", node.value());
        }
    });

    view! {
        <input node_ref=node_ref/>
    }
}

在这里的副作用(Effect)内部,node 只是一个 web_sys::HtmlInputElement。这允许我们调用任何相应的方法。

Inside the effect here, node is simply a web_sys::HtmlInputElement. This allows us to call any appropriate methods.

(请注意,.get() 在这里返回一个 Option,因为在实际创建 DOM 元素并填充之前,NodeRef 是空的。副作用在组件运行后的一个 tick 运行,因此在大多数情况下,当副作用运行时,<input> 已经创建好了。)

(Note that .get() returns an Option here, because the NodeRefis empty until it is filled when the DOM elements are actually created. Effects run a tick after the component runs, so in most cases the` will already have been created by the time the effect runs.)