Javascript is required
rust体验
        flowchart LR
0["概念"]
0 --> WASM
0 --> Rust
0 --> JavaScript
Rust --> rustc
Rust --> rustup
Rust --> wasm-pack
Rust --> Cargo

      

WebAssembly是什么?

WebAssembly (wasm) 是一种简单的机器模型和具有 广泛规范 的可执行格式。 它被设计为便携、紧凑,并以或接近原生速度执行。

作为一种编程语言,WebAssembly 由两种格式组成

  1. .wat 文本格式
  2. .wasm二进制格式是较低级别的

WASM 开发语言的选择

要写 WASM 应用的话首先不能选用有 GC 的语言,不然垃圾收集器的代码也会占用很大一部分的体积,对 WASM 文件的初始化加载并不友好,比较好的选择就是 C/C++/Rust 这几个没有 GC 的语言,当然使用 Go、C#、TypeScript 这些也是可以的,但是性能也会没有 C/C++/Rust 这么好。

Rust 的官方和社区对于 WASM 都有着一流的支持,而且它也是一门系统级编程语言,有一个和 NPM 一样好用的包管理器 Cargo,同时 Rust 也拥有着很好的性能,用来写 WASM 再好不过了。同时它的社区热度也在不断的上升中。

Rust 提供了对 WASM 一流的支持,Rust 无需 GC 、零运行时开销的特点也让它成为了 WASM 的完美候选者。Rust 是怎么编译成 WASM 代码的:

从零开始 WASM 项目并打包发布

➜ rustc --version
rustc 1.70.0 (90c541806 2023-05-31)

一个专门用于打包、发布 WASM 的工具,可以用于构建可在 NPM 发布的 WASM 工具包。当我们开发完 WASM 模块时,可以直接使用 wasm-pack publish 命令把我们开发的 WASM 包发布到 NPM 上。使用 cargo install wasm-pack 命令来进行安装。

➜ wasm-pack                                                                                    
📦 ✨  pack and publish your wasm!

Usage: wasm-pack [OPTIONS] <COMMAND>

Commands:
  build    🏗️  build your npm package!
  pack     🍱  create a tar of your npm package but don't publish!
  new      🐑 create a new project with a template
  publish  🎆  pack up your npm package and publish!
  login    👤  Add an npm registry user account! (aliases: adduser, add-user)
  test     👩‍🔬  test your wasm!
  help     Print this message or the help of the given subcommand(s)

Options:
  -v, --verbose...             Log verbosity is based off the number of v used
  -q, --quiet                  No output printed to stdout
      --log-level <LOG_LEVEL>  The maximum level of messages that should be logged by wasm-pack. [possible values: info, warn, error] [default: info]
  -h, --help                   Print help
  -V, --version                Print version
  
➜  wasm-pack --version
wasm-pack 0.12.1
cargo new example --lib

cd example
npm init -y

Hello World

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));
}

然后在 Cargo.toml 文件中添加 wasm-bindgen 依赖,wasm-bindgen来提供 JavaScript 和 Rust 类型之间的异常桥梁,允许使用 JavaScript 字符串调用 Rust API,或调用 Rust 函数来捕获 JavaScript。

[package]
name = "hello-wasm"
version = "0.1.0"

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

[dependencies]
wasm-bindgen = "0.2.89"

构建

WebAssembly 构建产物将会输出在 pkg 目录下

wasm-pack build

├── README.md
├── hello_wasm.d.ts
├── hello_wasm.js
├── hello_wasm_bg.js
├── hello_wasm_bg.wasm
├── hello_wasm_bg.wasm.d.ts
└── package.json

如果想当 npm 包发布的话,可以添加 —scope 参数,将会在 pkg 下生成 package.json 文件用于发布或当做一个 npm 包来使用,这样也可以在前端工程中直接当做一个模块来导入使用。

wasm-pack build --scope mynpmusername

借助 wasm-pack 可以非常轻松的将 rust 打包成 wasm,同时还提供了 js 相关支持。直接打包成 js 可导入的 npm 包,而不是让用户导入 wasm 文件然后通过浏览器 WebAssembly 对象来加载 WebAssembly 代码,其他语言的 WebAssembly 开发也是如此。

运行

编辑src/index.js

async function main() {
    const module = await import('../pkg/hello_wasm.js');
    module.greet("Hello Rs");
}
main();

根目录index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>WASM Hello World</title>
  </head>
  <body></body>
  <script src="js/index.js"></script>
</html>

vite.config.js

import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
import { defineConfig } from 'vite'

export default defineConfig({
    plugins: [
        wasm(),
        topLevelAwait()
    ],
    server: {
        host: '0.0.0.0',
        port: 3000,
    },
});

这时候就可以通过 js 直接导入使用

const js = import("./hello-wasm/hello_wasm.js");
js.then(js => {
  js.greet("WebAssembly");
});

console

src/lib.rs

use wasm_bindgen::prelude::*;
use web_sys::console;

#[wasm_bindgen]
pub fn greet() {
    console::log_1(&JsValue::from_str("Hello World!"));
}

js/index.js

async function main() {
    const module = await import('../pkg/hello_wasm.js');
    module.greet();
}
main();

Cargo.toml


[package]
categories = ["wasm"]
description = ""
edition = "2021"
name = "hello-wasm"
version = "0.1.0"

[lib]
# 一个动态的系统库将会产生,类似于C共享库。当编译一个从其它语言加载调用的动态库时这属性将会被使用
crate-type = ["cdylib"]

[features]

[dependencies]
# 用于将实体从 Rust 绑定到 JavaScript,或反过来。
# 提供了 JS 和 WASM 之间的通道,用来传递对象、字符串、数组这些数据类型
wasm-bindgen = "0.2.83"
wee_alloc = {version = "0.4.5", optional = true}

# web-sys 可以和 JS 的 API 进行交互,比如 DOM
[dependencies.web-sys]
features = ["console"]
version = "0.3.60"

[dev-dependencies]
# 用于所有JS环境 (如Node.js和浏览器)中的 JS 全局对象和函数的绑定
js-sys = "0.3.60"

# 0 – 不优化
# 1 – 基础优化
# 2 – 更多优化
# 3 – 全量优化,关注性能时建议开启此项
# s – 优化二进制大小
# z – 优化二进制大小同时关闭循环向量,关注体积时建议开启此项
[profile.dev]
debug = true
# link time optimize LLVM 的链接时间优化,false 时只会优化当前包,true/fat会跨依赖寻找关系图里的所有包进行优化
# 其它选项还有 off-关闭优化,thin是fat的更快版本
lto = true
opt-level = 'z'

[profile.release]
debug = false
lto = true
opt-level = 'z'

Rust 实现 MD5 算法

包查询

https://crates.io/

https://docs.rs/md5/latest/md5/

https://crates.io/crates/digest

加入依赖

Cargo.toml

[dependencies]
...
md5="0.7.0"

看到使用方法

src/lib.rs

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn md5(input: &str)-> String {
    let digest = md5::compute(input);
    format!("{:x}", digest)
}

前端调用

反调试

通过 wasm 可以有效的将代码隐藏起来

发布NPM

Cargo.toml,并在 name 后面加上你的包名

[package]
name = "hello-wasm-taoya7"
wasm-pack login
wasm-pack publish

错误

Error: wasm32-unknown-unknown target not found in sysroot

https://github.com/rustwasm/wasm-pack/issues/579

RUSTUP_USE_CURL=1 rustup update

解决办法: 切换 版本

参考

https://juejin.cn/post/7219613068275449893

https://fuwenhao.club/en/rust-wasm-md5/#%E4%BD%BF%E7%94%A8-wasm-pack-%E6%89%93%E5%8C%85-rust-%E4%B8%BA-wasm-%E

https://github.com/rwasm

https://rustwasm.github.io/docs/book/introduction.html

6%96%87%E4%BB%B6

https://webassembly.github.io/wabt/demo/wat2wasm/