Rust Web 全栈开发(十一):WebAssembly 尝鲜

发布于:2025-07-28 ⋅ 阅读:(14) ⋅ 点赞:(0)

Rust Web 全栈开发(十一):WebAssembly 尝鲜

参考视频:https://www.bilibili.com/video/BV1RP4y1G7KF

继续之前的 Actix 项目。

我们已经实现了一个 Web App,在网页端查看并操作数据库中教师的数据。现在我们想创建一个 WebAssembly App,查看并操作数据库中课程的数据。

在这里插入图片描述

什么是 WebAssembly?

WebAssembly 是一种新的编码方式,可以在现代浏览器中运行。

  • 它是一种低级的类汇编语言
  • 具有紧凑的二进制格式
  • 可以接近原生的性能运行
  • 并为 C/C++、C#、Rust 等语言提供一个编译目标,以便它们可以在 Web 上运行
  • 它也被设计为可以与 JavaScript 共存,允许两者一起工作

WebAssembly 不是汇编语言,它不针对特定的机器,而是中间编译器目标,针对浏览器。

在这里插入图片描述

WebAssembly 有两种格式:

  • 文本格式,后缀为 .wat
  • 二进制格式,后缀为 .wasm

WebAssembly 能做什么?

  • 可以把你编写 C/C++、C#、Rust 等语言的代码编译成 WebAssembly 模块
  • 你可以在 Web 应用中加载该模块,并通过 JavaScript 调用它
  • 它并不是为了替代 JS,而是与 JS 一起工作
  • 仍然需要 HTML 和 JS,因为 WebAssembly 无法访问平台 API,例如 DOM、WebGL …

在这里插入图片描述

一个 C 语言的例子:

在这里插入图片描述

  • 快速、高效、可移植:通过利用常见的硬件能力,WebAssembly 代码在不同平台上能够以接近本地速度
    运行。

  • 可读、可调试:WebAssembly 是一门低阶语言,但是它有确实有一种人类可读的文本格式(其标
    准最终版本目前仍在编制),这允许通过手工来写代码,看代码以及调试代码。

  • 保持安全:WebAssembly 被限制运行在一个安全的沙箱执行环境中。像其他网络代码一样,它遵循浏览器的同源策略和授权策略。

  • 不破坏网络:WebAssembly 的设计原则是与其他网络技术和谐共处并保持向后兼容。

安装 wasm-pack 和 cargo-generate

官方文档:https://rustwasm.github.io/docs/book/game-of-life/setup.html

  • wasm-pack 是构建、测试和发布 Rust 生成的 WebAssembly 的一站式商店。

  • cargo-generate 通过利用已有的 git 存储库作为模板,帮助你快速启动并运行新的 Rust 项目。

命令行执行以下两个命令:

cargo install wasm-pack
cargo install cargo-generate

使用项目模板

官方文档:https://rustwasm.github.io/docs/book/game-of-life/hello-world.html

项目模板预先配置了相同的默认值,因此你可以快速构建、集成和打包 Web 代码。

回到 Actix 项目的最顶层,在终端中用下面的命令克隆项目模板:

cargo generate --git https://github.com/rustwasm/wasm-pack-template

这里我一直 git 不下来:

在这里插入图片描述

就直接下载 zip,解压到 Actix 项目的最顶层了。

在这里插入图片描述

因为是手动添加的 wasm-pack-template,需要将其手动添加为 members:

在这里插入图片描述

先来看看 wasm-pack-template 的 Cargo.toml:

[package]
name = "{{project-name}}"
version = "0.1.0"
authors = ["{{authors}}"]
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]

[dependencies]
wasm-bindgen = "0.2.84"

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.7", optional = true }

[dev-dependencies]
wasm-bindgen-test = "0.3.34"

[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"

其中最核心的是依赖中的 wasm-bindgen,用于绑定生成器。

修改一下字段:

在这里插入图片描述

再来看 src 目录下的 lib.rs,这是我们要编译到 WebAssembly 的 Rust crate 的根文件。它使用 wasm-bindgen 与 JavaScript 进行交互。它导入 JavaScript 函数 alert,并导出 Rust 函数 greet,该函数会发出问候消息的警报。

mod utils;

use wasm_bindgen::prelude::*;

#[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-pack-template!");
}

在 Rust 中,想要调用前端的函数,需要这样声明:

#[wasm_bindgen]
extern {
    fn alert(s: &str);
}

Rust 函数想要被前端调用,需要加上 #[wasm_bindgen]。

在前端中,想要调用 Rust 的函数,需要这样声明:

#[wasm_bindgen]
pub fn greet() {
    alert("Hello, wasm-pack-template!");
}

将 alert 中的字符串改为 “Hello, wasm-pack-template!”。

utils.rs 模块提供了常用的实用程序,使 Rust 更容易编译到 WebAssembly。例如,在调试 wasm 代码时,这个文件很有用,但是我们现在可以忽略这个文件。

构建项目

我们使用 wasm-pack 编排以下构建步骤:

  • 确保我们有 Rust 1.30 或更新的版本,并且通过 Rust 安装了 wasm32-unknown-unknown 目标。

  • 通过 cargo 将 Rust 源代码编译成 WebAssembly .wasm 二进制文件。

  • 使用 wasm-bindgen 来生成 JavaScript API,以便使用 Rust 生成的 WebAssembly。

要完成所有这些,cd 到 wasm-pack-template 目录下,执行命令:

wasm-pack build

构建成功:

在这里插入图片描述

当构建完成后,我们可以在 pkg 目录中找到它的工件:

在这里插入图片描述

下面讲解几个重要的文件。

wasm-pack-template/pkg/wasm-pack-template_bg.wasm:由 Rust 编译器从 Rust 源代码生成的 WebAssembly 二进制文件。它包含所有 Rust 函数和数据的编译到 wasm 的版本。例如,它有一个导出的 greet 函数。

wasm-pack-template/pkg/wasm-pack-template.js:

import * as wasm from "./wasm_pack_template_bg.wasm";
export * from "./wasm_pack_template_bg.js";
import { __wbg_set_wasm } from "./wasm_pack_template_bg.js";
__wbg_set_wasm(wasm);
wasm.__wbindgen_start();

wasm-pack-template/pkg/wasm-pack-template.js 是包含 JavaScript 的胶水代码,由 wasm-bindgen 生成。它包含 JavaScript glue,用于将 DOM 和 JavaScript 函数导入 Rust,并将 WebAssembly 中的函数暴露给 JavaScript。

例如,有一个 JavaScript greet 函数,它包装了从 WebAssembly 模块导出的 greet 函数。现在,这种粘合并没有做太多的事情,但是当我们开始在 wasm 和 JavaScript 之间来回传递更多值时,它将帮助引导这些值跨越边界。

这里有问题,我把 wasm-pack-template/pkg/wasm-pack-template.js 改成了这样:

// import * as wasm from "./wasm_pack_template_bg.wasm";
import * as wasm from "./wasm_pack_template";
export * from "./wasm_pack_template_bg.js";
// import { __wbg_set_wasm } from "./wasm_pack_template_bg.js";
// __wbg_set_wasm(wasm);
// wasm.__wbindgen_start();
export function greet() {
    alert("Hello, wasm-pack-template!");
}

wasm-pack-template/pkg/wasm-pack-template.d.js:

/* tslint:disable */
/* eslint-disable */
export function greet(): void;

此类 .d.js 文件包含用于 JavaScript glue 的 TypeScript 类型声明。如果你使用 TypeScript,你可以检查调用 WebAssembly 函数的类型。如果你不使用 TypeScript,你可以安全地忽略这个文件。

wasm-pack-template/pkg/package.json:

{
  "name": "wasm-pack-template",
  "type": "module",
  "collaborators": [
    "xiye <812288728@qq.com>"
  ],
  "version": "0.1.0",
  "files": [
    "wasm_pack_template_bg.wasm",
    "wasm_pack_template.js",
    "wasm_pack_template_bg.js",
    "wasm_pack_template.d.ts"
  ],
  "main": "wasm_pack_template.js",
  "types": "wasm_pack_template.d.ts",
  "sideEffects": [
    "./wasm_pack_template.js",
    "./snippets/*"
  ]
}

package.json 文件包含生成的 JavaScript 和 WebAssembly 包的元数据。它被 npm 和 JavaScript 打包器用来确定包之间的依赖关系、包名、版本和其他一些东西。它帮助我们集成 JavaScript 工具,并允许我们将包发布到 npm。

生成网页

要获取 wasm-pack-template 包并在 Web 页面中使用它,我们需要使用 create-wasm-app 这个 JavaScript 项目模板。

首先下载 create-wasm-app 这个库:

npm install -g create-wasm-app

在 wasm-pack-template 目录下运行这个命令:

npm init wasm-app www

构建成功:

在这里插入图片描述

在 wasm-pack-template 目录下生成了一个 www 目录,与 pkg 目录并列:

在这里插入图片描述

让我们仔细看看其中的一些文件。

wasm-pack-template/www/package.json:

{
  "name": "create-wasm-app",
  "version": "0.1.0",
  "description": "create an app to consume rust-generated wasm packages",
  "main": "index.js",
  "bin": {
    "create-wasm-app": ".bin/create-wasm-app.js"
  },
  "scripts": {
    "build": "webpack --config webpack.config.js",
    "start": "webpack-dev-server"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/rustwasm/create-wasm-app.git"
  },
  "keywords": [
    "webassembly",
    "wasm",
    "rust",
    "webpack"
  ],
  "author": "Ashley Williams <ashley666ashley@gmail.com>",
  "license": "(MIT OR Apache-2.0)",
  "bugs": {
    "url": "https://github.com/rustwasm/create-wasm-app/issues"
  },
  "homepage": "https://github.com/rustwasm/create-wasm-app#readme",
  "devDependencies": {
    "hello-wasm-pack": "^0.1.0",
    "webpack": "^4.29.3",
    "webpack-cli": "^3.1.0",
    "webpack-dev-server": "^3.1.5",
    "copy-webpack-plugin": "^5.0.0"
  }
}

这个 package.json 预先配置了 webpack 和 webpack-dev-server 依赖项,以及对 hello-wasm-pack 的依赖项,它是发布到 npm 的初始 wasm-pack-template 包的一个版本。

wasm-pack-template/www/webpack.config.js:

const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require('path');

module.exports = {
  entry: "./bootstrap.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bootstrap.js",
  },
  mode: "development",
  plugins: [
    new CopyWebpackPlugin(['index.html'])
  ],
};

该文件配置 webpack 及其本地开发服务器。它是预先配置的,你根本不需要调整它来让 webpack 和它的本地开发服务器工作。

wasm-pack-template/www/index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello wasm-pack!</title>
  </head>
  <body>
    <noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
    <script src="./bootstrap.js"></script>
  </body>
</html>

这是 Web 页面的根 HTML 文件。除了加载 bootstrap.js 之外,它没有做太多的事情,这是 index.js 的一个非常薄的包装。

wasm-pack-template/www/index.js:

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

wasm.greet();

这是 Web 页面 JavaScript 的主要入口点。它导入 hello-wasm-pack npm 包,其中包含默认的 wasm-pack-template 的编译 WebAssembly 和 JavaScript glue,然后调用 hello-wasm-pack 的 greet 函数。

安装依赖项

首先,通过在 wasm-pack-template/www 子目录下运行 npm install,确保本地开发服务器及其依赖已经安装:

在这里插入图片描述

这个命令只需要运行一次,就会安装 webpack JavaScript 打包器和它的开发服务器。

注意,使用 Rust 和 WebAssembly 并不需要 webpack,它只是我们为了方便而选择的打包器和开发服务器。Parcel 和 Rollup 还应该支持将 WebAssembly 作为 ECMAScript 模块导入。如果你愿意,你也可以使用 Rust 和 WebAssembly 而不使用捆绑器。

在 www 中使用本地的 wasm-pack-template 包

向 wasm-pack-template/www/package.json 中添加依赖:

  "dependencies": {
    "wasm-pack-template": "file:../pkg"
  },

修改 wasm-pack-template/www/index.js,不使用 hello-wasm-pack 中的 greet 函数,而使用我们的 greet 函数:

// import * as hello_wasm from "hello-wasm-pack";
import * as wasm from "wasm-pack-template";

wasm.greet();

因为依赖有修改,需要再次在 wasm-pack-template/www 子目录下运行 npm install:

在这里插入图片描述

现在,我们的 Web 页面现在可以在本地访问了!

本地服务

接下来,为开发服务器打开一个新终端。在新终端中运行服务器可以让它在后台运行,并且不会阻止我们同时运行其他命令。在新的终端中,从 wasm-pack-template/www 目录中运行以下命令:

npm run start

构建成功:

在这里插入图片描述

将 Web 浏览器导航到 localhost:8081/,应该会看到一条警告消息:

在这里插入图片描述

任何时候进行更改并希望它们反映在 localhost:8081/ 上,只需在 wasm-pack-template 目录中重新运行 wasm-pack build 命令。


网站公告

今日签到

点亮在社区的每一天
去签到