commit/eac83fdf1281a9cee92358942cb61641d48a4564

Hello, World!

本节将会为你展示如何构建并运行你的第一个Rust和WebAssembly程序:一个带有警告弹窗“Hello, World!”的网页。

确保在开始之前你已经跟随搭建说明进行了其中的操作。。

克隆项目模板

项目模板带有预先配置的合理默认值,是的你可以快速构建整合和打包你的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文件为Rust的包管理器和构建工具cargo指定了依赖和元信息。 它预先配置了一个wasm-bindgen依赖,一些可选的依赖我们稍后介绍,并且正确初始化crate-type以生成.wasm库。

wasm-game-of-life/src/lib.rs

src/lib.rs文件是我们将要编译为WebAssembly的Rust包的根。 它使用wasm-bindgen与JavaScript进行互操作。 它导入了window.alertJavaScript函数,并导出greetRust函数,这提示一个问候消息。

extern crate cfg_if;
extern crate wasm_bindgen;

mod utils;

use cfg_if::cfg_if;
use wasm_bindgen::prelude::*;

cfg_if! {
    // 当`wee_alloc`特性启用的时候,使用`wee_alloc`作为全局分配器。
    if #[cfg(feature = "wee_alloc")] {
        extern crate 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模块提供了常用的工具使得将Rust编译为WebAssembly的工作更容易。 在之后的教程中我们将详细了解这些工具,比如当我们在调试我们的wasm代码一节, 不过目前我们可以忽略这个文件。

构建项目

我们使用wasm-pack来编排下面的构建步骤:

  • 确保我们已经拥有Rust1.30或者更新的版本,并且wasm32-unknown-unknown已经通过rustup完成安装,
  • 通过cargo将我们的Rust源代码编译为WebAssembly的.wasm二进制文件,
  • 使用wasm-bindgen生成JavaScript的API以使用我们的Rust生成的WebAssembly。

为了完成上述所有,在项目目录内运行这个命令:

wasm-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源代码编译而来的WebAssembly二进制文件。 它包含编译到wasm版本的所有Rust函数和数据。 例如,它有一个被导出的“greet”函数。

wasm-game-of-life/pkg/wasm_game_of_life.js

.js文件是由wasm-bindgen生成的,并且包含JavaScript胶水代码, 胶水代码用于导入DOM和JavaScript函数到Rust并对JavaScript暴露一个很好的WebAssembly函数的API。 例如,有一个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 <[email protected]>"
  ],
  "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预先配置了webpackwebpack-dev-server依赖, 也依赖于hello-wasm-pack,这是一个已经发布在npm上的wasm-pack-template包的初始化版本。

wasm-game-of-life/www/webpack.config.js

这个文件配置了webpack和它的本地开发服务。 它是预先配置好的,你根本不需要进行调整就能使webpack和它的本地开发服务工作。

wasm-game-of-life/www/index.html

这是Web页面的根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是我们的Web页面的JavaScript的主要入口点。 它导入了hello-wasm-pack npm包, 其中包含默认的wasm-pack-template的已编译WebAssembly和JavaScript胶水代码, 然后它调用hello-wasm-packgreet函数。

import * as wasm from "hello-wasm-pack";

wasm.greet();

安装依赖

首先,在wasm-game-of-life/www子目录内运行 npm install以确保本地开发服务和它的依赖已经安装:

npm install

这个命令只需要运行一次就能安装webpack的JavaScript绑定和它的开发服务。

注意,webpack不是Rust和WebAssembly所要求的,它只是我们这里为了方便所选择的绑定和开发服务。 Parcel和Rollup应该也支持导入WebAssembly作为ECMAScript模块。

www中使用我们的本地wasm-game-of-life

我们想要使用我们的本地wasm-game-of-life包而不是npm上的hello-wasm-pack包。 这允许我们增量地开发我们的Game of Life程序。

首先,在wasm-game-of-life/pkg目录内运行npm link, 使得本地包不用将他们发布到npm就可以被其他本地包依赖:

npm link

🐞 你运行npm link时遇到EACCESS或者权限错误了吗? [如何阻止npm的权限错误。] (https://docs.npmjs.com/getting-started/fixing-npm-permissions)

然后,通过在wasm-game-of-life/www目录内运行这个命令, 来在www包中使用npm link过的wasm-game-of-life版本:

npm link wasm-game-of-life

最后,修改wasm-game-of-life/www/index.js来导入wasm-game-of-life包而不是hello-wasm-pack包:

import * as wasm from "wasm-game-of-life";

wasm.greet();

我们的Web页面现在准备好本地服务了。

本地服务

接下来,为开发服务打开一个新的终端。 在新终端中运行服务使得我们让它在后台运行,而不会阻碍我们同时运行其他命令。 新终端中,在wasm-game-of-life/www目录下运行这个命令:

npm run start

将你的Web浏览器导航到http://localhost:8080/ 你应该能得到一个警示弹窗的问候信息:

Screenshot of the "Hello, wasm-game-of-life!" Web page alert

任何使用你做出了改变并想要反映在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/ 你应该看到一个自定义的问候消息!

    答案

    wasm-game-of-life/src/lib.rs中新版本的greet函数:

    
      #[wasm_bindgen]
      pub fn greet(name: &str) {
          alert(&format!("Hello, {}!", name));
      }
      

    wasm-game-of-life/www/index.js中新的greet调用:

    wasm.greet("Your Name");

results matching ""

    No results matching ""