@[TOC] # demo ## 编译 Rust 为 WebAssembly📕作者简介: 过去日记,致力于Java、GoLang,Rust等多种编程语言,热爱技术,喜欢游戏的博主。
📘相关专栏Rust初阶教程、go语言基础系列、spring教程等,大家有兴趣的可以看一看
📙Java并发编程系列,设计模式系列、go web开发框架 系列正在发展中,喜欢Java,GoLang,Rust,的朋友们可以关注一下哦!
在本教程中,我们将使用 Rust 的 npm 包构建工具 wasm-pack 来构建一个 npm 包。这个包只包含 WebAssembly 和 JavaScript 代码,以便包的用户无需安装 Rust 就能使用。他们甚至不需要知道这里包含 WebAssembly!
安装 Rust 环境
安装 Rust
前往 Install Rust 页面并跟随指示安装 Rust。这里会安装一个名为“rustup”的工具,这个工具能让你管理多个不同版本的 Rust。默认情况下,它会安装用于惯常 Rust 开发的 stable 版本 Rust Release。Rustup 会安装 Rust 的编译器 rustc、Rust 的包管理工具 cargo、Rust 的标准库 rust-std 以及一些有用的文档 rust-docs。
wasm-pack
要构建我们的包,我们需要一个额外工具 wasm-pack。它会帮助我们把我们的代码编译成 WebAssembly 并制造出正确的 npm 包。使用下面的命令可以下载并安装它:
cargo install wasm-pack
安装 Node.js 并获取 npm 账户
在这个例子中我们将会构建一个 npm 包,因此你需要确保安装 Node.js 和 npm 已经安装。另外,我们将会把包发布到 npm 上,因此你还需要一个 npm 账号。它们是免费的.
在 npm signup page 注册 npm 账户,并填写表格。
接下来,在命令行中运行 npm adduser:
> npm adduser
Username: yournpmusername
Password:
Email: (this IS public) you@example.com
Logged in as yournpmusername on https://registry.npmjs.org/.
如果并未正常运行,请联系 npm 解决。
构建我们的 WebAssembly npm 包
万事俱备,来创建一个新的 Rust 包吧。打开你用来存放你私人项目的目录,做这些事:
$ cargo new --lib hello-wasm
Created library `hello-wasm` project
这里会在名为 hello-wasm 的子目录里创建一个新的库,里面有下一步之前你所需要的一切:
+-- Cargo.toml
+-- src
+-- lib.rs
首先,我们有一个 Cargo.toml 文件,这是我们配置构建的方式。
接下来,Cargo 在 src/lib.rs 生成了一些 Rust 代码:
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
我们完全不需要使用这些测试代码,所以继续吧,我们删掉它。
让我们在 src/lib.rs 写一些代码替换掉原来的:
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
使用 wasm-bindgen 在 Rust 与 JavaScript 之间通信
第一部分看起来像这样:
extern crate wasm_bindgen;
use wasm_bindgen::prelude:😗;
第一行就像在说“哇 Rust,我们在用一个叫做 wasm_bindgen 的库”。在 Rust 当中,库被称为“crates”,因为我们使用的是一个外部库,所以有 “extern”。
第三行包括了一个将库中的代码引入到你的代码中的使用命令。
wasm-pack 使用另一个工具 wasm-bindgen 来提供 JavaScript 和 Rust 类型之间的桥梁。它允许 JavaScript 使用字符串调用 Rust API,或调用 Rust 函数来捕获 JavaScript 异常。
在 Rust 中调用来自 JavaScript 的外部函数
接下来的部分看起来像这样:
#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}
在 #[] 中的内容叫做 “属性”,并以某种方式改变下面的语句。在这种情况下,下面的语句是一个 extern,它将告诉 Rust that 我们想调用一些外部定义的函数。这个属性告诉我们 “wasm-bindgen 知道如何找到这些函数”。
第三行是用 Rust 写的函数签名。它告诉我们 “alert 函数接受一个叫做 s 的字符串作为参数。”
当你想调用新的 JavaScript 函数时,你可以在这里写他们,wasm-bindgen 将负责为你设置一切。
编写能够在 JavaScript 中调用的 Rust 函数
最后一部分是这样的:
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
我们又看到了 #[wasm_bindgen] 属性。在这里,它并非定义一个 extern 块,而是 fn,这代表我们希望能够在 JavaScript 中使用这个 Rust 函数。这和 extern 正相反:我们并非引入函数,而是要把函数给外部世界使用。
这个函数的名字是 greet,它需要一个参数,一个字符串(写作 &str)。它调用了我们前面在 extern 块中引入的 alert 函数。它传递了一个让我们串联字符串的 format! 宏的调用。
format! 在这里有两个参数,一个格式化字符串和一个要填入的变量。格式化字符串是 “Hello, {}!” 部分。它可以包含一个或多个 {},变量将会被填入其中。传递的变量是 name,也就是这个函数的参数。所以当我们调用 greet(“Steve”)时我们就能看到 “Hello, Steve!”。
上述字符串被传递到了 alert(),所以当我们调用这个函数时,我们应该能看到一个消息框弹出,其中的内容为“Hello, Steve!”。
把我们的代码编译到 WebAssembly
为了能够正确的编译我们的代码,首先我们需要配置 Cargo.toml。打开这个文件,将内容改为如下所示:
Copy to Clipboard
[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"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
你需要改为自己的仓库,同时 Cargo 需要通过 git 来完善 authors 部分。
最重要的是添加底下的部分。第一个部分 — [lib] — 告诉 Rust 为我们的包建立一个 cdylib 版本;在本教程中我们不会讲解它的含义。
第二个部分是 [dependencies] 部分。在这里我们告诉 Cargo 我们需要依赖哪个版本的 wasm-bindgen ;在这个例子中,它是 0.2.z 版本的 (不是 0.3.0 或者其他版本)。
构建包
现在我们已经完成了所有配置项,开始构建吧!在命令行输入以下命令:
wasm-pack build --scope mynpmusername
这个命令将做一系列事情 (这会花一些时间,特别是当你第一次运行 wasm-pack)。
把我们的包发布到 npm
把我们的新包发布到 npm registry:
cd pkg
npm publish --access=public
我们现在有了一个 npm 包,使用 Rust 编写,但已经被编译为 WebAssembly 了。现在这个包已经可以被 JavaScript 使用了,而且使用它完全不需要用户安装 Rust;包中的代码是 WebAssembly 代码,而不是 Rust 源码!
在网站上使用我们的包
让我们建立一个使用我们包的网站!人们通过各种打包工具使用 npm 包,在本教程中,我们将使用 webpack。它比其他某些打包工具稍微复杂一点,但展示了更实际的用法。
让我们离开pkg目录,并创建一个新目录site,尝试以下操作:
cd ../..
mkdir site
cd site
创建一个新文件 package.json,然后输入如下代码:
{
"scripts": {
"serve": "webpack-dev-server"
},
"dependencies": {
"@mynpmusername/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",
};
现在我们需要一个 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>
最后,从 HTML 文件中引用index.js:
const js = import("./node_modules/@yournpmusername/hello-wasm/hello_wasm.js");
js.then((js) => {
js.greet("WebAssembly");
});
这将从node_modules文件夹导入我们的模块。这不是最佳做法,但这里只做一个演示,因此暂时就这样用。加载后,它将从该模块调用greet函数,并传入字符串“WebAssembly”参数。注意这里看上去没有什么特别的,但是我们正在调用 Rust 代码!就 JavaScript 代码所知,这只是一个普通模块。
我们已经完成了所有的文件!让我们试一下:
npm install
npm run serve
这将启动一个 Web 服务器。访问 http://localhost:8080,你应该会在屏幕上看到一个内容为 Hello, WebAssembly! 的警告框。我们已经成功地从 JavaScript 调用了 Rust,并从 Rust 调用了 JavaScript。