如果你有一些 Rust 代码,你可以将它编译成WebAssembly (wasm)。本教程将带您了解将 Rust 项目编译为 wasm 并在现有 Web 应用程序中使用它所需的所有知识。
Rust 和 WebAssembly 用例
Rust 和 WebAssembly 有两个主要用例:
构建一个完整的应用程序——一个基于 Rust 的整个 Web 应用程序。
构建应用程序的一部分——在现有的 JavaScript 前端中使用 Rust。
目前,Rust 团队专注于后一种情况,这就是我们在这里介绍的内容。对于前一种情况,请查看以下项目yew.
在本教程中,我们使用 构建一个包wasm-pack,这是一个在 Rust 中构建 JavaScript 包的工具。这个包将只包含 WebAssembly 和 JavaScript 代码,因此包的用户不需要安装 Rust。他们甚至可能没有注意到它是用 Rust 编写的。
Rust 环境设置
让我们完成所有必需的步骤来设置我们的环境。
安装 Rust
转到安装 Rust安装 Rust页并按照说明进行操作。这会安装一个名为“rustup”的工具,它可以让你管理多个版本的 Rust。默认情况下,它会安装最新的稳定版 Rust,您可以将其用于一般的 Rust 开发。Rustup 安装rustc,Rust 编译器,以及cargoRust 的包管理器,rust-stdRust 的标准库,以及一些有用的文档 — rust-docs.
注意:注意bin系统中需要cargo目录的安装后说明PATH。这是自动添加的,但您必须重新启动终端才能使其生效。
wasm包
要构建包,我们需要一个额外的工具,wasm-pack. 这有助于将代码编译为 WebAssembly,并生成正确的打包以供在浏览器中使用。要下载并安装它,请在终端中输入以下命令:
$ cargo install wasm-pack
构建我们的 WebAssembly 包
让我们在 Rust 中创建一个新包。导航到您保存个人项目的位置,然后输入:
$ cargo new --lib hello-wasm Created library `hello-wasm` project
这将在一个名为的子目录中创建一个新库,其中hello-wasm包含您需要的所有内容:
+-- Cargo.toml
+-- src
+-- lib.rs
首先,我们有Cargo.toml;这是我们用来配置构建的文件。如果您使用Gemfile过 Bundler 或package.jsonnpm,这可能很熟悉;Cargo 的工作方式与他们两者相似。
接下来,Cargo 为我们生成了一些 Rust 代码src/lib.rs:
#[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } }
请把这个小屎山删除, 写一些Rust程序
将这段代码放入src/lib.rs:
use wasm_bindgen::prelude::*; #[wasm_bindgen] extern { pub fn alert(s: &str); } #[wasm_bindgen] pub fn greet(name: &str) { alert(&format!("Hello, {}!", name)); }
这是我们 Rust 项目的内容。它有三个主要部分;让我们依次讨论它们。我们在这里给出一个高层次的解释,并掩饰一些细节;要了解有关 Rust 的更多信息,请查看免费在线书籍Rust 编程语言.
用于Rustwasm-bindgen和 JavaScript 之间的通信
第一部分如下所示:
use wasm_bindgen::prelude::*;
库在 Rust 中称为“crates”。
这个套路你懂否? Cargo装的都是crates.
第一行包含一个use命令,它将库中的代码导入到您的代码中。在这种情况下,我们将导入wasm_bindgen::prelude模块中的所有内容。我们将在下一节中使用这些功能。
在我们进入下一节之前,我们应该多谈谈wasm-bindgen.
wasm-pack使用wasm-bindgen另一个工具,在 JavaScript 和 Rust 类型之间架起一座桥梁。它允许 JavaScript 使用字符串调用 Rust API,或调用 Rust 函数来捕获 JavaScript 异常。
我们在我们的包中使用wasm-bindgen's 功能。事实上,这是下一节。
从 Rust 调用 JavaScript 中的外部函数
下一部分如下所示:
#[wasm_bindgen] extern { pub fn alert(s: &str); }
内部的位#[ ]称为“属性”,它以某种方式修改下一条语句。在这种情况下,该语句是一个extern,它告诉 Rust 我们想要调用一些外部定义的函数。该属性表示“wasm-bindgen 知道如何找到这些函数”。
第三行是函数签名,用 Rust 编写。它说“alert函数接受一个参数,一个名为 . 的字符串s。”
您可能会怀疑,这是JavaScript 提供的alert功能。我们在下一节中调用这个函数。
每当您想调用 JavaScript 函数时,您都可以将它们添加到此文件中,并wasm-bindgen为您完成所有设置。并非所有内容都受支持,但我们正在努力。请文件错误如果缺少某些东西。
生成 JavaScript 可以调用的 Rust 函数
最后一部分是这个:
#[wasm_bindgen] pub fn greet(name: &str) { alert(&format!("Hello, {}!", name)); }
再一次,我们看到了#[wasm_bindgen]属性。在这种情况下,它不是修改一个extern块,而是一个fn; 这意味着我们希望这个 Rust 函数能够被 JavaScript 调用。它的反面extern:这些不是我们需要的功能,而是我们提供给世界的功能。
这个函数被命名为greet,并接受一个参数,一个字符串(写入&str),name。然后它调用我们在上面的块中要求的alert函数。extern它传递一个对format!宏的调用,它允许我们连接字符串。
在这种format!情况下,宏接受两个参数,一个格式字符串和一个要放入其中的变量。格式字符串是"Hello, {}!"位。它包含{}s,其中将插入变量。我们传递的变量是name函数的参数,所以如果我们调用greet("Steve")我们应该看到"Hello, Steve!".
这被传递给alert(),所以当我们调用这个函数时,我们会看到一个带有“你好,史蒂夫!”的警告框。在里面。
现在我们的库已经写好了,让我们来构建它。
将我们的代码编译为 WebAssembly
为了正确编译我们的代码,我们首先需要使用Cargo.toml. 打开此文件,并将其内容更改为如下所示:
[package] name = "hello-wasm"version = "0.1.0" authors = ["Your Name <you@example.com>"] description = "A sample project with wasm-pack" license = "MIT/Apache-2.0" repository = "https://github.com/yourgithubusername/hello-wasm"edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2"
git填写您自己的存储库并使用与该authors字段相同的信息。
要添加的重要部分是[package]. 该[lib]部分告诉 Rust 构建cdylib我们包的一个版本;我们不会在本教程中讨论这意味着什么。更多信息,请咨询货物和锈连杆文档。
最后一节是[dependencies]节。这里是我们告诉 Cargowasm-bindgen我们想要依赖哪个版本的地方;在这种情况下,这是任何0.2.z版本(但不是0.3.0或更高版本)。
构建包
现在我们已经完成了所有设置,让我们构建包。在终端中输入:
$ wasm-pack build --target web
这会做很多事情(它们会花费很多时间,尤其是第一次运行时wasm-pack)。要详细了解它们,请查看这篇关于 Mozilla Hacks 的博文. 简而言之,wasm-pack build:
将你的 Rust 代码编译为 WebAssembly。
在该 WebAssembly 上运行wasm-bindgen,生成一个 JavaScript 文件,该文件将该 WebAssembly 文件包装到浏览器可以理解的模块中。
创建一个pkg目录并将该 JavaScript 文件和您的 WebAssembly 代码移动到其中。
读取您的Cargo.toml并生成等效的package.json.
README.md将您的(如果有的话)复制到包装中。
最终结果?您在pkg目录中有一个包。
关于代码大小的题外话
如果您查看生成的 WebAssembly 代码大小,它可能是几百 KB。我们根本没有指示 Rust 优化大小,这样做会大大减少大小。这超出了本教程的范围,但是如果您想了解更多信息,请查看 Rust WebAssembly 工作组的文档缩小 .wasm 大小.
在网络上使用包
现在我们已经编译好了 wasm 模块。让我们在浏览器中运行它。
index.html让我们首先在项目的根目录中创建一个名为的文件,并为其提供以下内容:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hello-wasm example</title> </head> <body> <script type="module"> import init, {greet} from "./pkg/hello_wasm.js"; init().then(() => { greet("WebAssembly") }); </script> </body> </html>
该文件中的脚本会导入js胶水代码,初始化wasm模块,调用greet我们在rust中写的函数。
使用本地 Web 服务器为项目的根目录提供服务(例如python3 -m http.server)。如果您不确定如何执行此操作,请参阅运行简单的本地 HTTP 服务器。
注意:确保使用支持application/wasmMIME 类型的最新 Web 服务器。较旧的 Web 服务器可能还不支持它。
从 Web 服务器加载index.html, 举个例子, 如果您电脑里支持python3, 则在代码根目录路径用命令行输入
python3 -m http.server
进到locahost:8000端口页面, 屏幕上会出现一个警告框,其中包含Hello, WebAssembly!。我们已经成功地从 JavaScript 调用到 Rust,并从 Rust 调用到 JavaScript。
使我们的包对 npm 屎山系统可用
如果你想将 WebAssembly 模块与 npm 屎山一起使用,我们需要进行一些更改。
让我们从使用 target bundler 选项重新编译 Rust 开始:
$ wasm-pack build --target bundler
安装 Node.js 和npm屎山管理
我们正在构建一个 npm 包,因此您需要安装 Node.js 和 npm。
要获取 Node.js 和 npm,请转到获取 npm!页面并按照说明进行操作。选择版本时,请选择您喜欢的任何版本;本教程不是特定于版本的。
接下来,让我们使用 `npm link` 使这个包对安装的其他 JavaScript 包可用
$ cd pkg $ npm link
我们现在有一个 npm 包,用 Rust 编写,但编译为 WebAssembly。它可以从 JavaScript 中使用,并且不需要用户安装 Rust;包含的代码是 WebAssembly 代码,而不是 Rust 源代码。
在网络上使用npm屎山构造包
让我们建立一个使用我们新的npm屎山构造包的网站。许多人通过各种打包工具使用 npm 包,我们将webpack在本教程中使用其中之一。它只是有点复杂,并显示了一个现实的用例。
让我们移出该pkg目录,并创建一个新目录 ,site来尝试一下:
$ cd ..$ mkdir site $ cd site $ npm link hello-wasm
创建一个新文件 ,package.json并将以下代码放入其中:
{ "scripts": { "serve": "webpack-dev-server" }, "dependencies": { "hello-wasm": "^0.1.0" }, "devDependencies": { "webpack": "^4.25.1", "webpack-cli": "^3.1.2", "webpack-dev-server": "^3.1.10" } }
接下来,我们需要配置 Webpack。创建webpack.config.js并放入以下内容:
const path = require('path'); module.exports = { entry: "./index.js", output: { path: path.resolve(__dirname, "dist"), filename: "index.js", }, mode: "development" };
接下来,创建一个名为 的文件index.js,并为其提供以下内容:
import("./node_modules/hello-wasm/hello_wasm.js").then((js) => { js.greet("WebAssembly with NPM");});
node_modules这将从文件夹中导入新模块。这不被认为是最佳实践,但这是一个演示,所以现在可以了。加载后,它会greet从该模块调用函数,并"WebAssembly"作为字符串传递。请注意这里没有什么特别之处,但我们正在调用 Rust 代码。就 JavaScript 代码而言,这只是一个普通的模块。
最后,我们需要修改HTML文件;打开index.html文件并将当前内容替换为以下内容:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hello-wasm example</title> </head> <body> <script src="./index.js"></script> </body> </html>
我们完成了制作文件。让我们试一试:
$ npm install $ npm run serve
这将启动一个 Web 服务器。加载http://localhost:8080并在屏幕上出现一个警告框,其中包含Hello, WebAssembly with NPM!。我们已经成功地将 Rust 模块与 npm 一起使用。
结论
我们的教程到此结束;我们希望您发现它有用。