Hello, World!
本节将向你展示如何构建和运行你的第一个 Rust 和 WebAssembly 程序:一个警告“Hello,World!”
开始之前,请确保已按照安装说明进行操作。
克隆项目模板
项目模板预先配置了 sane 默认值,因此你可以快速构建、集成和打包Web代码。
使用以下命令克隆项目模板:
cargo generate --git https://github.com/rustwasm/wasm-pack-template
这将提示你输入新项目的名称。我们将使用 “wasm-game-of-life”。
wasm-game-of-life
里面的内容
输入命令进入新创建的项目 wasm-game-of-life
:
cd wasm-game-of-life
让我们看看它的内容:
wasm-game-of-life/
├── Cargo.toml
├── LICENSE_APACHE
├── LICENSE_MIT
├── README.md
└── src
├── lib.rs
└── utils.rs
让我们详细地看一下这些文件吧。
wasm-game-of-life/Cargo.toml
Cargo.toml
文件是 Cargo
、Rust
的包管理器和构建工具指定依赖项和元数据。这一个预先配置了一个wasm bindgen
依赖项、几个我们稍后将深入研究的可选依赖项,以及为生成 .wasm
库而正确初始化的 crainte type
。
wasm-game-of-life/src/lib.rs
src/lib.rs
文件是我们正在编译到 WebAssembly 的 Rust crate
的根文件。它使用 wasm bindgen 与JavaScript 接口。它导入 window.alert
JavaScript 函数,并导出名为 greet
Rust 函数,该函数用于创建警告弹窗消息。
mod utils;
use wasm_bindgen::prelude::*;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet() {
alert("Hello, wasm-game-of-life!");
}
wasm-game-of-life/src/utils.rs
src/utils.rs
模块提供了公共实用程序,使处理编译到 WebAssembly 的 Rust 变得更容易。我们将在本教程后面的部分更详细地了解其中的一些实用程序,例如在调试 wasm 代码时,但现在可以忽略此文件。
构建项目
我们使用 wasm-pack
构建的步骤:
- 确保我们有 Rust 1.30 或更新版本,并且通过
rustup
安装了wasm32 unknown
target, - 经过
cargo
,将我们的 Rust 源代码编译成为 WebAssembly.wasm
的二进制文件, - 使用
wasm bindgen
生成 JavaScript API,以使用我们的 Rust-generated WebAssembly。
要执行所有这些操作,请在项目目录中运行以下命令:
wams-pack build
构建完成后,我们可以在pkg目录中找到它的工件,它应该有以下内容:
pkg/
├── package.json
├── README.md
├── wasm_game_of_life_bg.wasm
├── wasm_game_of_life.d.ts
└── wasm_game_of_life.js
其中 README.md
文件是从主项目复制的,其他文件是全新的。
wasm-game-of-life/pkg/wasm_game_of_life_bg.wasm
.wasm
文件是由 Rust 编译器从我们的 Rust 源代码生成的WebAssembly 二进制文件。它包含所有Rust函数和数据的编译到 wasm 版本。例如,它有一个导出的 greet
函数。
wasm-game-of-life/pkg/wasm_game_of_life.js
.js
文件是生产自 wasm-bindgen
和包含 JavaScript 的胶水层
,用于将 DOM 已经 JavaScript 函数导入到 Rust,并将一个优雅的 API 开放给 JavaScript 的 WebAssembly 函数。
例如,下面的 JavaScript greet
函数是从 WebAssembly 模块导出的 greet
函数。
现在,这个胶水层
的作用不大,但是当我们开始在 wasm 和 JavaScript 之间来回传递更多有趣的值时,它将帮助引导这些值越过边界。
import * as wasm from './wasm_game_of_life_bg';
// ...
export function greet() {
return wasm.greet();
}
wasm-game-of-life/pkg/wasm_game_of_life.d.ts
.d.ts
文件包含了 JavaScript 胶水层的 TypeScript 类型声明。
如果你使用了 TypeScript,你可以检查对 WebAssembly 函数的调用类型,还有你的 IDE 可以根据你提供的类型文件进行类型推导。如果不使用TypeScript,可以安全地忽略此文件。
export function greet(): void;
wasm-game-of-life/pkg/package.json
package.json
文件包含有关生成的JavaScript和WebAssembly包的元数据。。
npm 和 JavaScript 使用它来确定包、包名、版本还有其他内容之间的依赖关系。
它帮助我们与 JavaScript 工具集成,并允许我们将包发布到npm。
{
"name": "wasm-game-of-life",
"collaborators": [
"Your Name <your.email@example.com>"
],
"description": null,
"version": "0.1.0",
"license": null,
"repository": null,
"files": [
"wasm_game_of_life_bg.wasm",
"wasm_game_of_life.d.ts"
],
"main": "wasm_game_of_life.js",
"types": "wasm_game_of_life.d.ts"
}
把它放到网页上
为了将我们的 wasm-game-of-life
包用于 Web 页面,我们可以使用 create-wasm-app
JavaScript项目模板。
在 wasm-game-of-life
里面运行命令:
npm init wasm-app www
下面是我们新的 wasm-game-of-life/www
子目录包含的内容:
wasm-game-of-life/www/
├── bootstrap.js
├── index.html
├── index.js
├── LICENSE-APACHE
├── LICENSE-MIT
├── package.json
├── README.md
└── webpack.config.js
再来一次,让我们仔细看看这些文件中的一些。
wasm-game-of-life/www/package.json
package.json
配置了 webpack
和 webpack-dev-server
依赖以及对 hello-wasm-pack
的依赖关系,它是已发布到 npm 的初始 wasm-pack-templatek
的一个版本。
wasm-game-of-life/www/webpack.config.js
这个文件配置了 webpack 以及本地开发服务器。 它是预先配置好的,不需要任何调整或配置即可在本地运行了。
wasm-game-of-life/www/index.html
这是网页的根文件,它除了加载 bootstrap.js
之外,它没有做其他操作。这是 index.js
的一个较小的封装。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello wasm-pack!</title>
</head>
<body>
<script src="./bootstrap.js"></script>
</body>
</html>
wasm-game-of-life/www/index.js
index.js
是网页 JavaScript 的主要入口点。
它导入 hello-wasm-pack
npm 包,其中包含默认 wasm-pack
模板的编译 WebAssembly 和 JavaScript 胶水层,然后调用 hello-wasm-pack
的 greet
函数。
import * as wasm from "hello-wasm-pack";
wasm.greet();
安装依赖
首先,通过在 wasm-game-of-life/www
子目录中运行 npm install
,确保安装了本地开发服务器及其依赖项:
npm install
此命令只需运行一次,将会安装 webpack JavaScript 依赖及其开发服务器。
请注意,Rust 和 WebAssembly 并不依赖
webpack
,它只是我们为方便起见选择的绑定器和开发服务器。Parcel and Rollup 还应支持将 WebAssembly 作为 ECMAScript 模块导入。如果你愿意,也可以使用不带捆绑程序的Rust
和WebAssembly
!
在 www
上使用我们本地的 wasm-game-of-life
包
我们不想使用npm的 hello-wasm-pack
,而是想使用我们本地的 wasm-game-of-life
包。
这将允许我们逐步发展我们程序。
打开文件 wasm-game-of-life/www/package.json
找到其中的 "dependencies"
字段,把 "wasm-game-of-life": "file:../pkg"
加到里面:
{
// ...
"dependencies": { // Add this three lines block!
"wasm-game-of-life": "file:../pkg"
},
"devDependencies": {
//...
}
}
然后引入 wasm-game-of-life
到文件 wasm-game-of-life/www/index.js
里面,而不是 hello-wasm-pack
包:
import * as wasm from "wasm-game-of-life";
wasm.greet();
既然我们新增了依赖,那我们就需要更新一下:
npm install
接着,我们的网页现在可以在本地运行了!
在本地运行
接下来,为开发服务器打开一个新终端。
在一个新的终端上运行服务器可以让它在后台运行,同时不会阻止我们运行其他命令。
在新终端中,从 wasm-game-of-life/www
目录中运行以下命令:
npm run start
打开浏览器进入 http://localhost:8080,将会出现弹窗警告信息。
更改之后,如果希望出现在 http://localhost:8080,就需要在 wasm-game-of-life
目录下重新运行一下命令 wasm-pack build
练习
我们可以修改 wasm-game-of-life/src/lib.rs
里面的函数 greet
,使用 name: &str
参数使弹窗信息可以自定义,把你的名字传给 wasm-game-of-life/www/index.js
中的 greet
函数。用 wasm-pack build
重新生成.wasm 二进制文件,然后在浏览器重新打开 http://localhost:8080 ,将会出现自定义的弹窗警告信息。
答案
将新的函数 greet
写在 wasm-game-of-life/src/lib.rs
:
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
可以使用调用方式
wasm.greet("Name");