WebAssembly入门与应用
约 1521 字大约 5 分钟
wasmwebassembly
2025-08-03
概述
WebAssembly(Wasm)是一种低级二进制指令格式,设计为可移植的编译目标,能在现代浏览器中以接近原生的速度执行。它与 JavaScript 互补而非替代,适合 CPU 密集型任务如图形处理、加密、游戏引擎和音视频编解码。
Wasm 在 Web 技术栈中的定位
1. WAT 文本格式
WAT(WebAssembly Text Format)是 Wasm 二进制格式的人类可读表示,使用 S-expression 语法。
;; add.wat — 两数相加
(module
;; 定义函数类型
(type $t0 (func (param i32 i32) (result i32)))
;; 定义函数
(func $add (type $t0) (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
;; 导出函数供 JavaScript 调用
(export "add" (func $add))
);; 阶乘示例
(module
(func $factorial (param $n i32) (result i32)
(if (result i32) (i32.le_s (local.get $n) (i32.const 1))
(then (i32.const 1))
(else
(i32.mul
(local.get $n)
(call $factorial (i32.sub (local.get $n) (i32.const 1)))
)
)
)
)
(export "factorial" (func $factorial))
)2. Module 结构
Wasm 数据类型
| 类型 | 说明 | 大小 |
|---|---|---|
| i32 | 32 位整数 | 4 bytes |
| i64 | 64 位整数 | 8 bytes |
| f32 | 32 位浮点 | 4 bytes |
| f64 | 64 位浮点 | 8 bytes |
| v128 | SIMD 向量 | 16 bytes |
| funcref | 函数引用 | - |
| externref | 外部引用 | - |
3. Linear Memory(线性内存)
Wasm 使用线性内存模型——一段连续的字节数组,可在 JavaScript 和 Wasm 之间共享。
// 创建共享内存
const memory = new WebAssembly.Memory({
initial: 1, // 1 页 = 64KB
maximum: 10, // 最大 10 页 = 640KB
shared: false // 是否共享(用于多线程)
});
// 通过 ArrayBuffer 访问
const buffer = new Uint8Array(memory.buffer);
buffer[0] = 42;
// 增长内存
memory.grow(1); // 增加 1 页// 在 Wasm 和 JS 之间传递字符串
function writeStringToMemory(memory, str, offset) {
const encoder = new TextEncoder();
const bytes = encoder.encode(str);
const view = new Uint8Array(memory.buffer, offset, bytes.length);
view.set(bytes);
return bytes.length;
}
function readStringFromMemory(memory, offset, length) {
const decoder = new TextDecoder();
const bytes = new Uint8Array(memory.buffer, offset, length);
return decoder.decode(bytes);
}4. JavaScript API
加载和实例化
// 方式 1: 流式编译(推荐,最高效)
const response = await fetch('module.wasm');
const { instance, module } = await WebAssembly.instantiateStreaming(response, {
env: {
memory: new WebAssembly.Memory({ initial: 1 }),
log: (value) => console.log('Wasm says:', value),
}
});
// 调用导出函数
const result = instance.exports.add(40, 2);
console.log(result); // 42
// 方式 2: 分步加载
const response2 = await fetch('module.wasm');
const bytes = await response2.arrayBuffer();
const module2 = await WebAssembly.compile(bytes);
const instance2 = await WebAssembly.instantiate(module2, imports);
// 方式 3: 同步编译(仅限小模块 + Worker)
const moduleSync = new WebAssembly.Module(bytes);
const instanceSync = new WebAssembly.Instance(moduleSync, imports);Import 和 Export
// 定义 imports(JavaScript 提供给 Wasm 的功能)
const importObject = {
env: {
// 函数导入
consoleLog: (ptr, len) => {
const str = readStringFromMemory(memory, ptr, len);
console.log(str);
},
// 内存导入
memory: new WebAssembly.Memory({ initial: 256 }),
// 表导入
table: new WebAssembly.Table({ initial: 4, element: 'anyfunc' }),
// 全局变量导入
__stack_pointer: new WebAssembly.Global({ value: 'i32', mutable: true }, 65536),
},
math: {
sin: Math.sin,
cos: Math.cos
}
};
const { instance } = await WebAssembly.instantiateStreaming(
fetch('module.wasm'),
importObject
);
// 使用导出
const { add, memory: wasmMemory, processData } = instance.exports;5. 从 C/Rust 编译到 Wasm
C + Emscripten
// math.c
#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
EMSCRIPTEN_KEEPALIVE
double distance(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return sqrt(dx * dx + dy * dy);
}# 编译命令
emcc math.c -o math.js \
-s EXPORTED_FUNCTIONS='["_fibonacci", "_distance"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' \
-O3// 使用 Emscripten 生成的 JS 胶水代码
Module.onRuntimeInitialized = () => {
const fibonacci = Module.cwrap('fibonacci', 'number', ['number']);
console.log(fibonacci(10)); // 55
const distance = Module.cwrap('distance', 'number',
['number', 'number', 'number', 'number']);
console.log(distance(0, 0, 3, 4)); // 5
};Rust + wasm-pack
// src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
#[wasm_bindgen]
pub struct Universe {
width: u32,
height: u32,
cells: Vec<u8>,
}
#[wasm_bindgen]
impl Universe {
pub fn new(width: u32, height: u32) -> Universe {
let cells = vec![0; (width * height) as usize];
Universe { width, height, cells }
}
pub fn tick(&mut self) {
// Game of Life 逻辑
}
pub fn cells_ptr(&self) -> *const u8 {
self.cells.as_ptr()
}
}wasm-pack build --target webimport init, { greet, Universe } from './pkg/my_wasm.js';
await init();
console.log(greet('World')); // "Hello, World!"
const universe = Universe.new(64, 64);
universe.tick();6. WASI(WebAssembly System Interface)
WASI 是 Wasm 的系统接口标准,使 Wasm 模块可以在浏览器之外运行(服务器、边缘计算、嵌入式设备等)。
// Node.js 中使用 WASI
import { WASI } from 'wasi';
import { readFile } from 'fs/promises';
const wasi = new WASI({
args: ['app', '--verbose'],
env: { NODE_ENV: 'production' },
preopens: { '/sandbox': '/real/path' } // 文件系统映射
});
const wasmBuffer = await readFile('app.wasm');
const module = await WebAssembly.compile(wasmBuffer);
const instance = await WebAssembly.instantiate(module, {
wasi_snapshot_preview1: wasi.wasiImport
});
wasi.start(instance);7. Component Model
Wasm Component Model 是下一代模块化标准,解决模块间互操作的类型安全问题。
// WIT (Wasm Interface Type) 定义
package example:image-processor;
interface processor {
record image {
width: u32,
height: u32,
data: list<u8>,
}
enum filter {
grayscale,
blur,
sharpen,
}
apply-filter: func(img: image, f: filter) -> image;
}
world image-app {
export processor;
}8. 实际应用案例
性能对比
// JavaScript vs WebAssembly 性能测试
// 斐波那契(大量递归)
// JavaScript 版本
function jsFibonacci(n) {
if (n <= 1) return n;
return jsFibonacci(n - 1) + jsFibonacci(n - 2);
}
// WebAssembly 版本
const wasmFibonacci = instance.exports.fibonacci;
// 基准测试
console.time('JS fibonacci(40)');
jsFibonacci(40);
console.timeEnd('JS fibonacci(40)'); // ~1200ms
console.time('Wasm fibonacci(40)');
wasmFibonacci(40);
console.timeEnd('Wasm fibonacci(40)'); // ~600ms (约2倍加速)
// 注意:V8 已经很快,Wasm 的优势主要在于:
// 1. 可预测的性能(无 JIT 编译波动)
// 2. 紧凑的二进制格式(加载快)
// 3. 利用已有的 C/C++/Rust 代码库总结
WebAssembly 提供了一种在 Web 平台上运行高性能代码的标准方式。它通过线性内存模型实现与 JavaScript 的互操作,支持 C/C++、Rust、Go 等多种语言编译为 Wasm 目标。WASI 将 Wasm 的适用范围扩展到服务器和边缘计算。虽然 Wasm 不会替代 JavaScript,但在计算密集型场景(图像处理、加密、游戏引擎、音视频编解码)中,它提供了显著的性能优势和代码复用能力。
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于