在为什么你应该在 WebAssembly 中使用 Rust?中,我探讨了你可能想要编写 WebAssembly (Wasm) 的原因,以及为什么你可能选择 Rust 作为执行此操作的语言。现在我将分享它的外观,方法是探索将 Rust 嵌入到 JavaScript 中的方法。
这是 Rust 与 Go、C# 和其他具有大型运行时且可以编译为 Wasm 的语言的区别之处。Rust 具有最小的运行时(基本上只是一个分配器),这使得从 JavaScript 库中使用 Rust 变得容易。C 和 C++ 也有类似的情况,但 Rust 的独特之处在于它的工具,我们现在将对其进行了解。
基础知识
如果你以前从未使用过 Rust,你首先需要进行设置。这非常容易。首先下载 Rustup,这是一种控制 Rust 版本和用于交叉编译的不同工具链的方法。这将使你可以访问 Cargo,它是 Rust 构建工具和包管理器。
现在我们需要做一个决定。我们可以轻松地编写通过 WebAssembly 在浏览器中运行的 Rust 代码,但是如果我们想做一些除了让人们的 CPU 风扇旋转之外的事情,我们可能在某个时候想要与文档对象模型 (DOM) 交互或使用一些 JavaScript API。换句话说:我们需要 JavaScript 互操作(也称为 JavaScript 互操作性 API)。
问题和解决方案
WebAssembly 是一种极其简单的机器语言。如果我们想要能够与 JavaScript 通信,Wasm 只提供了四种数据类型来执行此操作:32 位和 64 位浮点数和整数。Wasm 没有字符串、数组、对象或任何其他丰富数据类型的概念。基本上,我们只能在 Rust 和 JavaScript 之间传递指针。不用说,这远非理想。
好消息是,有两个库可以促进基于 Rust 的 Wasm 和 JavaScript 之间的通信:wasm-bindgen 和 stdweb。然而,坏消息是这两个库不幸地彼此不兼容。wasm-bindgen 比 stdweb 更底层,并试图提供对 JavaScript 和 Rust 如何交互的完全控制。事实上,甚至有关于使用 wasm-bindgen 重写 stdweb的讨论,这将消除不兼容性问题。
因为 wasm-bindgen 是更轻量级的选择(并且是官方 Rust WebAssembly 工作组 官方工作的选项),我们将重点关注它。
wasm-bindgen 和 wasm-pack
我们将创建一个函数,该函数从 JavaScript 中获取一个字符串,将其转换为大写并在其前面加上 "HELLO, ",然后将其返回给 JavaScript。我们将这个函数称为 excited_greeting!
首先,让我们创建我们的 Rust 库,它将包含这个出色的函数
$ cargo new my-wasm-library --lib
$ cd my-wasm-library
现在我们要用我们令人兴奋的逻辑替换 src/lib.rs 的内容。我认为最好写出代码而不是复制/粘贴。
// Include the `wasm_bindgen` attribute into the current namespace.
use wasm_bindgen::prelude::wasm_bindgen;
// This attribute makes calling Rust from JavaScript possible.
// It generates code that can convert the basic types wasm understands
// (integers and floats) into more complex types like strings and
// vice versa. If you're interested in how this works, check this out:
// https://blog.ryanlevick.com/posts/wasm-bindgen-interop/
#[wasm_bindgen]
// This is pretty plain Rust code. If you've written Rust before this
// should look extremely familiar. If not, why wait?! Check this out:
// https://doc.rust-lang.net.cn/book/
pub fn excited_greeting(original: &str) -> String {
format!("HELLO, {}", original.to_uppercase())
}
其次,我们必须对我们的 Cargo.toml 配置文件进行两项更改
- 添加 wasm_bindgen 作为依赖项。
- 将库二进制文件的类型配置为 cdylib 或动态系统库。在这种情况下,我们的系统是 wasm,设置此选项是我们生成 .wasm 二进制文件的方式。
[package]
name = "my-wasm-library"
version = "0.1.0"
authors = ["$YOUR_INFO"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2.33"
现在让我们构建!如果我们只使用 cargo build,我们将获得一个 .wasm 二进制文件,但是为了方便从 JavaScript 调用我们的 Rust 代码,我们希望有一些 JavaScript 代码将丰富的 JavaScript 类型(如字符串和对象)转换为指针,并将这些指针代表我们传递给 Wasm 模块。手动执行此操作既繁琐又容易出错。
幸运的是,除了作为一个库之外,wasm-bindgen 还具有为我们创建这种“粘合”JavaScript 的能力。这意味着在我们的代码中,我们可以使用普通的 JavaScript 类型与我们的 Wasm 模块交互,并且从 wasm-bindgen 生成的代码将完成将这些丰富的类型转换为 Wasm 实际理解的指针类型的繁琐工作。
我们可以使用出色的 wasm-pack 来构建我们的 Wasm 二进制文件,调用 wasm-bindgen CLI 工具,并将我们所有的 JavaScript(以及任何可选生成的 TypeScript 类型)打包到一个漂亮整洁的包中。现在让我们这样做!
首先我们需要安装 wasm-pack
$ cargo install wasm-pack
默认情况下,wasm-bindgen 生成 ES6 模块。我们将从一个简单的 script 标签中使用我们的代码,所以我们只希望它生成一个普通的 JavaScript 对象,该对象使我们可以访问我们的 Wasm 函数。为此,我们将向其传递 --target no-modules 选项。
$ wasm-pack build --target no-modules
我们现在在我们的项目中有一个 pkg 目录。如果我们查看其内容,我们将看到以下内容
- package.json:如果我们想将其打包为 NPM 模块,则很有用
- my_wasm_library_bg.wasm:我们实际的 Wasm 代码
- my_wasm_library.js:JavaScript “粘合”代码
- 一些 TypeScript 定义文件
现在我们可以创建一个 index.html 文件,它将使用我们的 JavaScript 和 Wasm
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
</head>
<body>
<!-- Include our glue code -->
<script src='./pkg/my_wasm_library.js'></script>
<!-- Include our glue code -->
<script>
window.addEventListener('load', async () => {
// Load the wasm file
await wasm_bindgen('./pkg/my_wasm_library_bg.wasm');
// Once it's loaded the `wasm_bindgen` object is populated
// with the functions defined in our Rust code
const greeting = wasm_bindgen.excited_greeting("Ryan")
console.log(greeting)
});
</script>
</body>
</html>
你可能会想在浏览器中打开 HTML 文件,但不幸的是,这是不可能的。出于安全原因,Wasm 文件必须从与 HTML 文件相同的域提供。你需要一个 HTTP 服务器。如果你有喜欢的静态 HTTP 服务器可以从你的文件系统提供文件,请随时使用它。我喜欢使用 basic-http-server,你可以像这样安装和运行它
$ cargo install basic-http-server
$ basic-http-server
现在通过访问 http://localhost:4000/index.html 在 Web 服务器中打开 index.html 文件,并检查你的 JavaScript 控制台。你应该在那里看到一个非常令人兴奋的问候!
如果你有任何问题,请 告诉我。下次,我们将了解如何从我们的 Rust 代码中使用各种浏览器和 JavaScript API。
评论已关闭。