WebAssembly(WASM)最初是为了在浏览器中运行C++而诞生的。到了2026年,它已经可以在各种环境中运行 - 浏览器、服务器、边缘网络、嵌入式设备 - 并为Web上最高负载的应用提供动力。Figma的渲染引擎、Web版Adobe Photoshop、Google Meet的视频处理以及Cloudflare的边缘计算平台都运行在WebAssembly之上。
根据Chrome Platform Status的数据,截至2026年初,WASM约占Chrome所有页面加载的5.5%,较前一年的4.5%有所增长。随着WASM 3.0成为W3C标准以及WASI向1.0版本成熟,整个生态系统已经到达了一个转折点。
本指南涵盖了你开始使用WebAssembly构建所需的一切知识。
什么是WebAssembly?
WebAssembly是一种作为编译目标而设计的二进制指令格式。你用高级语言(Rust、C、C++、Go、Kotlin)编写代码,编译为.wasm,然后在任何拥有WASM运行时的环境中运行 - 浏览器、Node.js、Cloudflare Workers、Wasmtime或Wasmer。
工作原理
WASM是一个基于栈的虚拟机。函数在操作数栈上推入和弹出值。宿主运行时(Chrome中的V8、Firefox中的SpiderMonkey)将WASM字节码JIT编译为原生机器码,因此性能接近原生水平。
关键特性:
- 沙箱执行:WASM模块只能访问宿主明确授权的资源。未经许可,无法访问文件系统、网络或操作系统。这与原生代码有根本性的不同。
- 线性内存:WASM和宿主之间共享的单个连续
ArrayBuffer。复杂数据(字符串、结构体)通过写入内存并共享指针来传递。 - 类型受限:WASM原生只支持四种类型:
i32、i64、f32、f64。其他所有类型(字符串、数组、对象)需要通过线性内存或Component Model进行编码。 - 可移植:同一个
.wasm二进制文件可以在任何拥有WASM运行时的平台上运行,无需重新编译。
WASM vs JavaScript
WASM不是用来替代JavaScript的,而是用来补充它的。
| 方面 | JavaScript | WebAssembly |
|---|---|---|
| 解析 | 运行时解析+编译 | 预编译二进制文件,仅需解码 |
| 执行速度 | JIT优化,性能波动 | 接近原生,性能稳定 |
| 启动 | 小脚本启动快 | 快速解码,可预测 |
| DOM访问 | 直接 | 间接(通过JS胶水代码) |
| 最适合 | UI、DOM操作、事件处理 | CPU密集型计算 |
| 垃圾回收 | 内置 | WasmGC(新)或手动管理 |
UI和DOM操作用JavaScript。重计算用WASM:图像处理、视频编码、物理模拟、加密、数据解析。
WASM 3.0:有什么新特性
WebAssembly 3.0于2025年9月成为W3C标准,标准化了历经多年开发的九项特性。
| 特性 | 实现的功能 |
|---|---|
| WasmGC | WASM中的原生垃圾回收。托管语言(Java、Kotlin、Dart)可以编译到WASM而无需携带自己的GC运行时。Chrome 119+、Firefox 120+、Safari 18.2+已支持。 |
| Exception Handling | WASM中的原生try/catch。此前,异常处理需要昂贵的JavaScript往返开销。 |
| Tail Calls | 实现无栈溢出的高效递归。对函数式语言至关重要。 |
| Relaxed SIMD | 用于并行数据处理的128位向量指令。支持硬件特定的优化。 |
| Memory64 | 突破4GB线性内存限制。大规模数据处理所必需。 |
| Multi-memory | 一个模块中包含多个独立的内存区域。 |
影响最大的是WasmGC。在此之前,将Java或Kotlin编译到WASM意味着需要将整个垃圾回收器作为二进制文件的一部分,导致文件体积膨胀。现在,浏览器自身的GC可以像处理JavaScript一样为WASM模块管理内存。
WASI:浏览器之外的WebAssembly
浏览器中的WASM已经很强大,但让WASM成为通用运行时的是WASI(WebAssembly System Interface)。WASI为系统资源 - 文件、网络、时钟、随机数 - 提供标准化接口,使WASM模块能够在浏览器之外运行。
WASI Preview 2(当前稳定版本)定义了以下接口:
wasi:filesystem- 通过能力句柄(而非传统文件描述符)进行文件操作wasi:sockets- TCP/UDP网络wasi:http- HTTP请求/响应处理wasi:clocks- 墙上时钟、单调时钟wasi:random- 加密随机数wasi:cli- 命令行参数、环境变量、标准输入输出
核心原则是基于能力的安全模型:WASM模块无法访问文件系统,除非宿主明确授予特定目录的句柄。这使得WASI从根本上比运行原生可执行文件更加安全。
通往WASI 1.0之路
WASI 0.3.0(添加async/并发原语)预计在2026年发布,随后将推出WASI 1.0。主要新增功能是带有零拷贝流式I/O的语言级异步支持。
Component Model
核心WASM模块只能交换数字。Component Model通过在WASM之上添加丰富的类型系统和可组合性层来解决这一限制。
WIT (WebAssembly Interface Types)
WIT是一种接口定义语言,允许组件使用丰富类型 - 字符串、记录、列表、变体、枚举 - 来声明其导入和导出,而不仅仅是i32和f64。
// calculator.wit package myorg:calculator@1.0.0; interface operations { record calculation { expression: string, result: f64, timestamp: u64, } add: func(a: f64, b: f64) -> f64; multiply: func(a: f64, b: f64) -> f64; history: func() -> list<calculation>; } world calculator { export operations; }
wit-bindgen等工具链可以从WIT文件生成特定语言的绑定。Rust组件和Python组件可以通过WIT契约交换字符串、记录和列表,而无需知道对方的实现语言。
使用Rust构建你的第一个WASM模块
Rust拥有最成熟的WASM工具链。让我们构建一个实际的例子:一个在浏览器中运行的图像处理模块。
环境配置
# Install the WASM target for Rust rustup target add wasm32-unknown-unknown # Install wasm-pack (builds Rust to WASM + generates JS bindings) cargo install wasm-pack # Create a new library project cargo new --lib image-processor cd image-processor
配置Cargo.toml
[package] name = "image-processor" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2"
编写Rust代码
// src/lib.rs use wasm_bindgen::prelude::*; /// Convert an image buffer to grayscale. /// Input: RGBA pixel data as a flat u8 array (4 bytes per pixel). /// Output: Modified in place for zero-copy performance. #[wasm_bindgen] pub fn grayscale(pixels: &mut [u8]) { for chunk in pixels.chunks_exact_mut(4) { let r = chunk[0] as f32; let g = chunk[1] as f32; let b = chunk[2] as f32; // ITU-R BT.709 luminance coefficients let gray = (0.2126 * r + 0.7152 * g + 0.0722 * b) as u8; chunk[0] = gray; chunk[1] = gray; chunk[2] = gray; // chunk[3] is alpha, leave unchanged } } /// Adjust brightness of an image. /// factor > 1.0 brightens, < 1.0 darkens. #[wasm_bindgen] pub fn adjust_brightness(pixels: &mut [u8], factor: f32) { for chunk in pixels.chunks_exact_mut(4) { chunk[0] = ((chunk[0] as f32 * factor).min(255.0)) as u8; chunk[1] = ((chunk[1] as f32 * factor).min(255.0)) as u8; chunk[2] = ((chunk[2] as f32 * factor).min(255.0)) as u8; } } /// Invert all colors in the image. #[wasm_bindgen] pub fn invert(pixels: &mut [u8]) { for chunk in pixels.chunks_exact_mut(4) { chunk[0] = 255 - chunk[0]; chunk[1] = 255 - chunk[1]; chunk[2] = 255 - chunk[2]; } } /// Calculate the average brightness of an image (0-255). #[wasm_bindgen] pub fn average_brightness(pixels: &[u8]) -> f32 { let mut total: f64 = 0.0; let pixel_count = pixels.len() / 4; for chunk in pixels.chunks_exact(4) { let luminance = 0.2126 * chunk[0] as f64 + 0.7152 * chunk[1] as f64 + 0.0722 * chunk[2] as f64; total += luminance; } (total / pixel_count as f64) as f32 }
构建
wasm-pack build --target web
这将生成一个pkg/目录,包含:
image_processor_bg.wasm- 编译后的WASM二进制文件image_processor.js- 附带TypeScript类型定义的JavaScript胶水代码package.json- 可直接发布到npm
在JavaScript中使用
<!DOCTYPE html> <html> <head><title>WASM Image Processor</title></head> <body> <canvas id="canvas" width="800" height="600"></canvas> <button onclick="applyGrayscale()">Grayscale</button> <button onclick="applyBrightness()">Brighten</button> <button onclick="applyInvert()">Invert</button> <script type="module"> import init, { grayscale, adjust_brightness, invert } from "./pkg/image_processor.js"; let ctx; let imageData; async function setup() { await init(); const canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); // Load an image onto the canvas const img = new Image(); img.onload = () => { ctx.drawImage(img, 0, 0); imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); }; img.src = "photo.jpg"; } window.applyGrayscale = () => { grayscale(imageData.data); ctx.putImageData(imageData, 0, 0); }; window.applyBrightness = () => { adjust_brightness(imageData.data, 1.3); ctx.putImageData(imageData, 0, 0); }; window.applyInvert = () => { invert(imageData.data); ctx.putImageData(imageData, 0, 0); }; setup(); </script> </body> </html>
关键点在于:imageData.data是一个由ArrayBuffer支持的Uint8ClampedArray。传递给WASM时,它共享同一块内存 - 不需要复制。Rust函数就地修改像素,JavaScript端可以立即看到变化。
低层级:手动WASM实例化
如果你不想使用wasm-bindgen,可以直接实例化WASM模块:
const response = await fetch("calculator.wasm"); const { instance } = await WebAssembly.instantiateStreaming(response, { env: { // Functions the WASM module can call log_result: (value) => console.log("Result:", value), }, }); // Call exported functions const { add, multiply } = instance.exports; console.log(add(5, 3)); // 8 console.log(multiply(4, 7)); // 28
当你需要最小开销且不需要丰富的类型互操作时,这很有用。
性能:WASM vs JavaScript
真实世界的基准测试显示,CPU密集型任务获得了显著的加速:
| 任务 | JavaScript | WASM | 加速比 |
|---|---|---|---|
| 4K图像处理 | 180ms | 8ms(使用SIMD) | 22倍 |
| 图像缩放(4K) | 250ms | 45ms | 5.5倍 |
| 物理模拟(1万实体) | 掉帧 | 流畅60fps | 约10倍 |
| JSON解析(大负载) | 12ms | 3ms | 4倍 |
| 加密哈希 | 45ms | 6ms | 7.5倍 |
WASM的运行速度约为原生代码的95%。最大的优势来自:
- 可预测的性能(无JIT预热、无GC暂停)
- 用于并行数据处理的SIMD指令
- 无垃圾回收器干扰的直接内存访问
WASM不会更快的场景:DOM操作、小规模计算、I/O密集型任务。JavaScript已经为这些场景进行了优化。
生产应用案例
Figma:实时矢量渲染
Figma的核心渲染引擎是用C++编译到WASM的。每个形状、渐变和效果都在WASM中计算并绘制到Canvas元素上。这使得Figma能够在浏览器中以60fps处理包含数千图层的复杂设计 - 这在纯JavaScript中是不可能实现的性能。
Web版Adobe Photoshop
Adobe使用Rust将Photoshop的关键滤镜和工具移植到了WASM。他们的基准测试显示,使用WASM SIMD进行4K图像处理仅需22ms,而JavaScript需要180ms - 提升了8倍,使得实时滤镜预览成为可能。
Cloudflare Workers
Cloudflare在遍布330多个边缘节点的V8隔离区中运行WASM模块。冷启动时间为1-5ms(相比基于容器的Serverless的100-500ms)。2026年2月,他们使用WASM在整个边缘网络上部署了Llama-3-8b推理。
Google Meet
Google Meet的背景模糊和虚拟背景使用带SIMD的WASM进行实时视频处理。WASM模块以足够快的速度处理每一帧视频,保持30fps的流畅视频。
浏览器支持(2026年)
| 特性 | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| 核心WASM | 完全 | 完全 | 完全 | 完全 |
| Threads | 支持 | 支持 | 支持 | 支持 |
| SIMD(128位) | 支持 | 支持 | 支持 | 支持 |
| WasmGC | 119+ | 120+ | 18.2+ | 支持 |
| Exception Handling | 支持 | 支持 | 支持 | 支持 |
| Memory64 | 支持 | 支持 | 部分 | 支持 |
所有主流浏览器都完全支持WASM。较新的特性(WasmGC、Exception Handling)已经广泛可用。
工具参考
| 工具 | 用途 | 安装方式 |
|---|---|---|
| wasm-pack | 将Rust构建为WASM,生成npm包 | cargo install wasm-pack |
| wasm-bindgen | Rust/JS互操作绑定(wasm-pack使用) | Cargo.toml中的依赖 |
| wasm-opt | 二进制大小优化(50%以上的缩减) | Binaryen的一部分:brew install binaryen |
| wit-bindgen | 从WIT文件生成绑定 | cargo install wit-bindgen-cli |
| Wasmtime | 服务端WASM运行时(WASI参考实现) | brew install wasmtime |
| Wasmer | 支持WASI的替代WASM运行时 | curl https://get.wasmer.io -sSfL | sh |
| wasm-feature-detect | 运行时浏览器特性检测 | npm install wasm-feature-detect |
优化二进制大小
WASM二进制文件可能很大。以下是缩减方法:
# Cargo.toml [profile.release] opt-level = "z" # Optimize for size lto = true # Link-time optimization codegen-units = 1 # Better optimization, slower compile strip = true # Strip debug symbols
# Build in release mode wasm-pack build --release --target web # Further optimize with wasm-opt wasm-opt -Oz pkg/image_processor_bg.wasm -o pkg/image_processor_bg.wasm
典型的Rust WASM模块经过这些优化后,可以从500KB缩减到50KB以下。
入门路线图
结语
WebAssembly不再是实验性技术。它是一项被Web上最高负载应用所采用的生产技术。接近原生的性能、沙箱安全性和通用可移植性 - 没有其他编译目标能同时提供这三者。
你不需要用WASM重写整个应用。从一个CPU密集型函数开始 - 一个图像滤镜、一个数据解析器、一个物理计算 - 编译为WASM,然后从JavaScript调用它。测量差异,然后决定WASM还能在哪里发挥作用。
工具已经成熟,浏览器支持已经普及,生态系统正在不断壮大。如果你在写Rust,你距离浏览器只差一个命令。
入门检查清单:
- 安装Rust和wasm-pack
- 构建第一个WASM模块并在浏览器中运行
- JavaScript互操作正常工作(从JS调用WASM)
- 应用大小优化的发布版本构建
- 与纯JavaScript等效实现进行性能基准测试
- 使用Wasmtime探索WASI的服务端用例