WebAssembly 入门

2026-06-22 · 6 阅读 · 540字
RustWebAssembly性能优化

WebAssembly 入门

什么是 WebAssembly

WebAssembly(简称 Wasm)是一种低级的二进制指令格式,可以在现代浏览器中以接近原生的速度运行。它不是 JavaScript 的替代品,而是与之互补——当需要高性能计算时,Wasm 是理想选择。

核心特点

  • 高性能:接近原生速度,比 JS 快数倍到数十倍
  • 类型安全:具有类型系统的二进制格式
  • 可移植:一次编译,任何支持 Wasm 的环境都能运行
  • 安全:在沙箱中运行,遵循浏览器的同源策略
  • 多语言:可用 C/C++、Rust、Go、Python 等语言编写

为什么需要 WebAssembly

JavaScript 虽然灵活,但在计算密集场景下存在性能瓶颈:

  • 图像/视频处理
  • 3D 渲染(游戏、CAD)
  • 加密解密
  • 数据压缩
  • 科学计算
  • AI/机器学习

快速上手(使用 Rust)

安装工具链

# 安装 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 添加 wasm 目标
rustup target add wasm32-unknown-unknown

# 安装 wasm-pack
cargo install wasm-pack

创建项目

cargo new --lib wasm-game-of-life
// src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
    match n {
        0 => 0,
        1 => 1,
        _ => fibonacci(n - 1) + fibonacci(n - 2),
    }
}

#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("你好, {}! 来自 WebAssembly 的问候", name)
}

编译

wasm-pack build --target web

在 JavaScript 中使用

import init, { fibonacci, greet } from './pkg/wasm_game_of_life.js';

async function main() {
  await init(); // 初始化 Wasm 模块
  console.log(greet('World')); // 你好, World! 来自 WebAssembly 的问候
  console.log(fibonacci(42)); // 267914296
}

main();

在 Go 中使用 WebAssembly

// main.go
package main

import "syscall/js"

func add(this js.Value, args []js.Value) interface{} {
    a := args[0].Int()
    b := args[1].Int()
    return a + b
}

func main() {
    c := make(chan struct{}, 0)
    js.Global().Set("addWasm", js.FuncOf(add))
    <-c // 阻止程序退出
}
GOOS=js GOARCH=wasm go build -o main.wasm main.go
// 加载 Go Wasm 模块
const go = new Go();
const result = await WebAssembly.instantiateStreaming(
  fetch('main.wasm'), go.importObject
);
go.run(result.instance);
console.log(addWasm(3, 4)); // 7

WebAssembly 与 JavaScript 交互

JS 调用 Wasm

// 加载 Wasm 模块
const response = await fetch('module.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
  env: {
    print: (n) => console.log(n),
  },
});

// 调用导出函数
instance.exports.myFunction(42);

// 访问内存
const memory = instance.exports.memory;
const view = new Int32Array(memory.buffer);

Wasm 调用 JS

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);

    #[wasm_bindgen(js_namespace = window)]
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn show_message(name: &str) {
    log(&format!("Hello, {}!", name));
    alert("来自 Wasm 的消息");
}

性能对比

// JavaScript 实现
function fibonacciJS(n) {
  if (n <= 1) return n;
  return fibonacciJS(n - 1) + fibonacciJS(n - 2);
}

// Rust (Wasm) 实现将在浏览器中运行
// fibonacci(42): JS ≈ 1800ms, Wasm ≈ 120ms
// Wasm 快了约 15 倍

使用场景

1. 图像处理

#[wasm_bindgen]
pub fn grayscale(pixels: &[u8], width: u32, height: u32) -> Vec<u8> {
    let mut result = Vec::with_capacity(pixels.len());
    for chunk in pixels.chunks(4) {
        let gray = (0.299 * chunk[0] as f32 + 0.587 * chunk[1] as f32 + 0.114 * chunk[2] as f32) as u8;
        result.push(gray);
        result.push(gray);
        result.push(gray);
        result.push(chunk[3]); // 保持 alpha
    }
    result
}

2. 数据压缩

// 使用 brotli 压缩库
#[wasm_bindgen]
pub fn compress_data(input: &[u8]) -> Vec<u8> {
    brotli::compress(input, 6, Default::default())
}

#[wasm_bindgen]
pub fn decompress_data(input: &[u8]) -> Vec<u8> {
    brotli::decompress(input, Default::default())
}

局限性

  • 无法直接操作 DOM:需要通过 JS 桥接
  • 启动开销:加载和初始化需要时间
  • 调试困难:比 JS 更难调试
  • 单线程:仍需 Web Workers 实现并发
  • 生态相对年轻:工具链和库不如 JS 丰富

未来发展方向

  • WASI(WebAssembly System Interface):将 Wasm 扩展到服务器端
  • 多线程支持:WebAssembly Threads 提案
  • GC 支持:让 Java、Kotlin、Dart 等语言能编译到 Wasm
  • 组件模型:模块化、可组合的 Wasm 组件

总结

WebAssembly 适合做 JS 不擅长的计算密集型任务。它不是要取代 JS,而是扩展 Web 平台的能力边界。对于前端开发者,了解 Wasm 的基础概念和使用方式,能让你在遇到性能瓶颈时多一个有力的工具。