跳过导航

wasn学习

学习 WebAssembly

**WebAssembly(简称 Wasm)** 是一种为浏览器和非浏览器环境设计的**低级二进制指令格式**,核心价值是让 C/C++、Rust 等语言能以**接近原生的性能**在 Web 上运行,作为 JavaScript 的高性能补充。

---

### 一、核心定义
- **本质**:基于栈式虚拟机的二进制字节码标准(非编程语言)
- **定位**:高级语言(C/C++/Rust/Go 等)的**编译目标**
- **标准**:W3C 开放标准,主流浏览器(Chrome/Firefox/Safari/Edge)原生支持
- **文件**`.wasm`(二进制)、`.wat`(文本格式,调试用)

### 二、核心特性
1. **极致性能**
   - 接近原生执行速度(可达原生 80%–90%)
   - 静态类型、紧凑二进制、解码/编译极快
   - 无 JS 垃圾回收停顿,适合计算密集任务

2. **安全沙箱**
   - 内存安全、线性内存隔离
   - 遵循浏览器同源策略,无直接系统调用

3. **跨平台/跨语言**
   - 一次编译,多平台(浏览器/Node.js/边缘计算)运行
   - 语言中立:支持 C/C++、Rust、Go、AssemblyScript 等

4. **与 JS 互操作**
   - Wasm ↔ JS 双向调用
   - 共享内存、通过 JS 访问 DOM/Web API

### 三、运行原理(极简)
1. **编译**:C/C++/Rust → 工具链(Emscripten/wasm-pack)→ `.wasm`
2. **加载**:JS 用 `WebAssembly.instantiate` 加载/实例化
3. **执行**:在沙箱虚拟机中运行,线性内存读写
4. **交互**:通过 JS 桥接 DOM、网络、I/O

### 四、典型应用场景
- **游戏**:Unity/Unreal 引擎 Web 移植、3D 渲染
- **图像/视频**:实时滤镜、4K 处理、编解码(如 Photopea)
- **科学计算**:物理模拟、AI 推理、密码学
- **桌面应用 Web 化**:Figma、VS Code Web 等
- **边缘计算**:Serverless 轻量级沙箱(Cloudflare Workers)

### 五、优势与局限
**优势**
- 性能远超 JS(计算密集场景快 5–10 倍+)
- 复用现有 C/C++/Rust 代码库
- 体积小、加载快、跨平台
- 安全沙箱、内存隔离

**局限**
- **不能直接操作 DOM**:必须经 JS 桥接(少量开销)
- 开发/调试比 JS 复杂
- 内存需手动管理(类似 C/C++)
- 适合**计算密集型**,普通 Web 业务仍以 JS 为主

### 六、一句话总结
**Wasm 让 Web 拥有原生级性能,是高性能 Web 应用、游戏、AI、音视频处理的关键技术,与 JS 互补而非替代。**

go demo

package main

import "syscall/js"

// 导出给 JS 调用的函数
func add(this js.Value, args []js.Value) interface{} {
	a := args[0].Int()
	b := args[1].Int()
	return a + b
}

func main() {
	// 注册函数到全局
	js.Global().Set("goAdd", js.FuncOf(add))
	// 阻塞常驻
	<-make(chan struct{})
}

执行命令编译wasm

# 编译出来 wasm
GOOS=js GOARCH=wasm go build -o main.wasm
# 下载可以引用的js
curl  https://raw.githubusercontent.com/golang/go/master/lib/wasm/wasm_exec.js -o wasm_exec.js

index.html

<!DOCTYPE html>
<html>
<body>
<script src="wasm_exec.js"></script>
<script>
async function run() {
  const go = new Go();
  const res = await fetch("main.wasm");
  const bytes = await res.arrayBuffer();
  const { instance } = await WebAssembly.instantiate(bytes, go.importObject);
  go.run(instance);

  // 测试调用
  console.log("Wasm 计算结果:", goAdd(10, 20));
}
run();
</script>
</body>
</html>

rust demo

新建rust项目

cargo new wasm-rust

# 装上编译工具
cargo install wasm-pack

写rust代码

[package]
name = "wasm-rust"
version = "0.1.0"
edition = "2024"

<!-- 新增下面的内容 -->
[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
wasm-bindgen = "0.2"  

删除 src/main.rs 文件 新增 src/lib.rs 文件

// 引入 wasm-bindgen
use wasm_bindgen::prelude::*;

// 导出函数给 JS 调用
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

// 也可以写字符串、复杂逻辑
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

## 1. `[lib]`
意思是:
**我这个项目不是可执行程序,而是一个库(library)**
- Wasm 永远是**库**,给 JS 调用
- 不是独立运行的 exe/app
所以必须声明 `[lib]`

---

## 2. `crate-type = ["cdylib"]` 🔥 最重要
这是**Wasm 必需的核心配置**

- `c` = C 语言风格的接口
- `dy` = dynamic 动态库
- `lib` = library

**作用:**
👉 让 Rust 编译出 **C 语言兼容格式的动态库**
👉 这种格式 **正好就是 WebAssembly 需要的格式**
👉 浏览器 / Node.js 才能加载它

**没有 cdylib → 无法生成 .wasm 文件**
这就是你刚才报错的根本原因。

---

## 3. `rlib`
这是**Rust 内部使用的库格式**
作用:
- 方便 Rust 之间互相依赖
- 方便调试、打包
- 对 Wasm 本身**不是必须**,但加上更兼容

编译

wasm-pack build --target web

index.html

<!DOCTYPE html>
<html>
<body>

<script type="module">
  // 直接导入编译好的 WASM 包
  import init from './pkg/wasm_rust.js';
  const { add, greet } = await init();

  // 调用 Rust 函数!
  console.log("加法结果:", add(10, 20));
  console.log("问候:", greet("Rust Wasm"));
</script>

</body>
</html>

AssemblyScript demo

AssemblyScript

本地运行 wasm

brew install wasmtime
......

z@zdeMacBook-Pro learn % wasmtime --version
wasmtime 44.0.1 (f302ebd6b 2026-04-30)
z@zdeMacBook-Pro learn % 

运行go 的wasm


z@zdeMacBook-Pro wasm-go $ GOOS=wasip1 GOARCH=wasm go build -o wasspi.wasm
package .
        imports syscall/js: build constraints exclude all Go files in /usr/local/go/src/syscall/js

查阅发现得重新写, 当前版本是支持前端页面的js调用的

package main

import "fmt"

func main() {
    fmt.Println("✅ Go WASI 后端 Wasm 运行成功!")
    fmt.Println("👉 运行在 wasmtime 虚拟机中")

    // 调用函数
    res := add(10, 20)
    fmt.Printf("🧮 10 + 20 = %d\n", res)
}

// 加法函数(可被调用)
func add(a, b int) int {
    return a + b
}
# 编译
z@zdeMacBook-Pro wasm-go $ GOOS=wasip1 GOARCH=wasm go build -o wasmapi.wasm wasm-api/main.go 

z@zdeMacBook-Pro wasm-go $ wasmtime wasmapi.wasm 
 Go WASI 后端 Wasm 运行成功!
👉 运行在 wasmtime 虚拟机中
🧮 10 + 20 = 30

z@zdeMacBook-Pro wasm-go $ wasmtime --invoke add wasmapi.wasm 10 20
Error: failed to run main module `wasmapi.wasm`

Caused by:
    no func export named `add` found

# 这里为什么失败 -- 这里没有导出

新增导出


// 加法函数(可被调用)
// 关键!
// 导出函数 add,给 Wasm 虚拟机调用
//
//go:wasmexport add
func add(a, b int) int {
   return a + b
}

继续编译

z@zdeMacBook-Pro wasm-go $ GOOS=wasip1 GOARCH=wasm go build -o wasmapi.wasm wasm-api/main.go 
# command-line-arguments
wasm-api/main.go:19:6: go:wasmexport: unsupported parameter type int
wasm-api/main.go:19:6: go:wasmexport: unsupported result type int

还是报错 查阅 这里只用 类型

  • i32
  • i64
  • f32
  • f64

// 加法函数(可被调用)
// 关键!
// 导出函数 add,给 Wasm 虚拟机调用
//
//go:wasmexport add
func add(a, b int32) int32 {
   return a + b
}

z@zdeMacBook-Pro wasm-go $ GOOS=wasip1 GOARCH=wasm go build -o wasmapi.wasm wasm-api/main.go 
z@zdeMacBook-Pro wasm-go $ wasmtime --invoke add wasmapi.wasm 10 20
warning: using `--invoke` with a function that takes arguments is experimental and may break in the future
runtime: wasmexport function called before runtime initialization
        call _start first
Error: failed to run main module `wasmapi.wasm`

Caused by:
    0: failed to invoke `add`
    1: error while executing at wasm backtrace:
    0: 0x117807 - <unknown>!runtime.abort
    1: 0x11a0c7 - <unknown>!runtime.notInitialized
    2: 0x177f4b - <unknown>!add
    2: wasm trap: wasm `unreachable` instruction executed

z@zdeMacBook-Pro wasm-go $ wasmtime wasmapi.wasm  --invoke add  10 20
 Go WASI 后端 Wasm 运行成功!
👉 运行在 wasmtime 虚拟机中
🧮 10 + 20 = 30
z@zdeMacBook-Pro wasm-go $ wasmtime wasmapi.wasm --invoke add 10 20
 Go WASI 后端 Wasm 运行成功!
👉 运行在 wasmtime 虚拟机中
🧮 10 + 20 = 30
z@zdeMacBook-Pro wasm-go $ wasmtime wasmapi.wasm 
 Go WASI 后端 Wasm 运行成功!
👉 运行在 wasmtime 虚拟机中
🧮 10 + 20 = 30
z@zdeMacBook-Pro wasm-go $ 

这里多次踩坑发现go 不能支持使用 invoke 调用函数 他只能支持使用main.go

运行rust 的wasm


z@zdeMacBook-Pro wasm-rust % rustup target add wasm32-wasip1
info: downloading component rust-std
     rust-std installed                       21.81 MiB  

    #  安装对应 平台的编译支持

         
z@zdeMacBook-Pro wasm-rust % cargo build --target wasm32-wasip1 --release
   Compiling wasm-bindgen-shared v0.2.121
   Compiling unicode-ident v1.0.24
   Compiling once_cell v1.21.4
   Compiling cfg-if v1.0.4
   Compiling wasm-bindgen v0.2.121
   Compiling wasm-rust v0.1.0 (/Users/z/Desktop/code/learn/wasm-rust)
    Finished `release` profile [optimized] target(s) in 1.97s


z@zdeMacBook-Pro wasm-rust % wasmtime --invoke add target/wasm32-wasip1/release/rust_wasi_demo.wasm 10 20
Error: failed to open wasm module "target/wasm32-wasip1/release/rust_wasi_demo.wasm"

Caused by:
    No such file or directory (os error 2)

z@zdeMacBook-Pro wasm-rust % wasmtime --invoke add target/wasm32-wasip1/release/wasm_rust.wasm 10 20
Error: failed to run main module `target/wasm32-wasip1/release/wasm_rust.wasm`

Caused by:
    0: failed to instantiate "target/wasm32-wasip1/release/wasm_rust.wasm"
    1: unknown import: `__wbindgen_placeholder__::__wbindgen_describe` has not been defined

# 这里也是相同的问题- 也是得修改代码,支持原生的 wasm 调用 原本的是 js调用支持

修改代码

#[cfg(feature = "wasm-bindgen")]
use wasm_bindgen::prelude::*;

// ─── wasm-bindgen mode (default, for wasm-pack) ───
#[cfg(feature = "wasm-bindgen")]
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(feature = "wasm-bindgen")]
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

// ─── Raw C-ABI mode (for wasm32-wasip1) ───
#[cfg(not(feature = "wasm-bindgen"))]
#[unsafe(no_mangle)]  // 👈 这里改了! 新特性要求!
pub extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(not(feature = "wasm-bindgen"))]
#[unsafe(no_mangle)]  // 👈 这里改了 新特性要求!
pub extern "C" fn greet(ptr: *const u8, len: usize) -> *mut u8 {
    let name = unsafe { std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr, len)) };
    let s = format!("Hello, {}!", name);
    let r = s.as_ptr() as *mut u8;
    std::mem::forget(s);
    r
}

Cargo.toml新增特性

[package]
name = "wasm-rust"
version = "0.1.0"
edition = "2024"

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

[features]
default = ["wasm-bindgen"]
wasm-bindgen = ["dep:wasm-bindgen"]

[dependencies]
wasm-bindgen = { version = "0.2", optional = true }  

为了支持两种编译


z@zdeMacBook-Pro wasm-rust % cargo build --target wasm32-wasip1 --release --no-default-features
   Compiling wasm-rust v0.1.0 (/Users/z/Desktop/code/learn/wasm-rust)
    Finished `release` profile [optimized] target(s) in 0.28s
z@zdeMacBook-Pro wasm-rust % wasmtime --invoke add target/wasm32-wasip1/release/wasm_rust.wasm 10 20
warning: using `--invoke` with a function that takes arguments is experimental and may break in the future
warning: using `--invoke` with a function that returns values is experimental and may break in the future
30
z@zdeMacBook-Pro wasm-rust % 

解释上面的 features


我给你用**最通俗、最清晰、最实用**的方式,把 **Cargo Features** 彻底讲明白!

# 一句话总结
```
[features]
就是给你的 Rust 项目做 **“功能开关”**
想开启哪块功能,编译时就打开,不用就关掉!
```

---

# 逐行解释你这段代码
```toml
[features]
default = ["wasm-bindgen"]          # 👈 默认开启的功能
wasm-bindgen = ["dep:wasm-bindgen"]  # 👈 定义这个功能依赖什么
```

## 1. `[features]`
作用:  
**声明项目有哪些可选功能**

---

## 2. `wasm-bindgen = ["dep:wasm-bindgen"]`
意思:
> 我定义一个 **功能开关**,名字叫 `wasm-bindgen`  
> 打开这个开关 → **自动引入 wasm-bindgen 依赖**

`dep:wasm-bindgen` 的意思:
> 启用 `Cargo.toml` 里的 `wasm-bindgen` 依赖包

---

## 3. `default = ["wasm-bindgen"]`
意思:
> **默认情况下,自动打开 wasm-bindgen 开关**

也就是:
- 你直接 `cargo build`  
- 就等于 `cargo build --features wasm-bindgen`

---

# 结合你的项目,到底在干嘛?
你的项目 **一套代码,两套用途**

## ✔ 模式 A:浏览器 / Web(默认)
```toml
default = ["wasm-bindgen"]
```
编译:
```bash
wasm-pack build --target web
```
效果:
- 启用 `#[wasm_bindgen]`
- 给 JS 调用
- **前端网页用**

---

## ✔ 模式 B:Wasmtime / 边缘计算(关闭默认)
编译:
```bash
cargo build --no-default-features --target wasm32-wasip1
```
效果:
- **关闭 wasm-bindgen**
- 启用 `#[unsafe(no_mangle)] extern "C"`
- 给 wasm 虚拟机调用
- **后端边缘计算用**

---

# 最直观的比喻
你的项目 = 一间房子

- **`default = ["wasm-bindgen"]`**  
  默认给你**配上网(浏览器模式)**
- **`--no-default-features`**  
  把网断掉,变成**纯本地服务器(Wasmtime模式)**

---

# 最终总结(背会这 3 句)
1. **`[features]` = 功能开关**
2. **`wasm-bindgen = ["dep:wasm-bindgen"]` = 定义一个开关,打开就加载依赖**
3. **`default = ["wasm-bindgen"]` = 默认打开浏览器模式**

---

# 你的项目为什么这样设计?
为了实现 **业界最顶级的 Wasm 架构:**

## **一套代码 = 前端 WebAssembly + 后端 WASI 边缘计算**
- 前端:`wasm-bindgen` on
- 后端:`wasm-bindgen` off