你大概率见过这些场景:
这时候 WebAssembly(Wasm / WASM) 基本都会进入候选名单。
但 WASM 的讨论经常会跑偏——要么只讲“性能很强”,要么只贴一堆工具链命令,最后你依然不清楚:
这篇文章按“能落地”的顺序讲:先把历史和核心模型讲清楚,再给你可复制的最小 Demo,最后补一段 WASI(“Wasm 跑出浏览器”)的现状与方向。
WASM 不是凭空出现的“第四语言”,它更像是浏览器厂商在一次长期工程博弈后达成的共识:要给 Web 一个“可移植的、接近原生性能的编译目标”。
在 WASM 之前,业界已经通过 asm.js 证明:只要把编译后的代码限制在一个更“规整”的子集里,JS 引擎就能做出很多 AOT/JIT 优化,让 C/C++ 这类语言“看起来像在浏览器里跑原生”。
asm.js 的意义不在于它有多优雅,而在于它把问题定义清楚了:性能不是完全没希望,但 JavaScript 作为载体有天然限制。
WASM 的核心动机可以用一句话概括:
既然我们想要的是“通用、紧凑、可验证、可优化”的低层表示,那就不要继续用 JavaScript 语法硬凑了。
于是 WASM 以 二进制格式(.wasm)的形态出现,成为更适合浏览器引擎优化的编译目标。
这是 WASM 走向“基础设施”的关键节点。
W3C 在新闻公告中强调 WebAssembly 提供的是一种 safe、portable、low-level code format,用于 efficient execution 与 compact representation;并明确它进入 Recommendation 后,成为开放 Web 平台的一部分,可以把 Web 推向更高的性能、效率与响应性。
如果你最近在追“Wasm 2.0 / 3.0”,很可能会看到一个现实:它不像传统标准那样每次都走一遍完整流程。
WebAssembly 官方规格索引页会把各版本对应到不同状态(例如 1.0 是 Recommendation;后续版本以 Candidate Recommendation Draft / live spec 的方式演进)。
你只需要记住一个工程结论:WASM 是“增量提案 + 分层标准化”的路线,这让它既能保持兼容性,也能持续吸收 SIMD、GC、异常处理等能力(细节不在本文展开)。
把 WASM 当成“浏览器里的一个安全虚拟机”会更接近真实。
你真正调用的是 instance.exports。
WASM 的关键安全思路是:
这和 eval()、原生插件时代最大的区别是:WASM 是能力受控的。
WASM 的数据交换常常不靠“传对象”,而靠共享一段线性内存:
WebAssembly.Memory这样做的好处是:跨边界传大块数据时,不必频繁拷贝。
WASM 官方安全模型也明确了两个目标:
来源:https://webassembly.org/docs/security/
现代浏览器里最常见的方式是 WebAssembly.instantiateStreaming():它能在 fetch 的响应流上直接编译和实例化 WASM,减少额外的 arrayBuffer() 复制步骤。
参考:MDN
WebAssembly.instantiateStreaming():https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/JavaScript_interface/instantiateStreaming_static
// browser.ts
const { instance } = await WebAssembly.instantiateStreaming(
fetch('/demo.wasm'),
{
// importObject:这里填你 wasm 模块声明的 imports
},
)
// instance.exports.xxx 就是 wasm 导出的函数/内存/表
console.log(instance.exports)你只要把握一件事:importObject 的结构必须与 wasm 模块声明的 imports 完全匹配,否则会在链接阶段报错。
MDN 提供了一个非常经典的例子:JS 创建 WebAssembly.Memory,再通过 instantiateStreaming 把这段内存传给 wasm;wasm 导出一个 accumulate 函数,对内存里的 i32 数组求和。
参考:MDN
WebAssembly.Memory:https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/JavaScript_interface/Memory
const memory = new WebAssembly.Memory({
initial: 10,
maximum: 100,
})
WebAssembly.instantiateStreaming(fetch('memory.wasm'), {
js: { mem: memory },
}).then((obj) => {
const summands = new DataView(memory.buffer)
for (let i = 0; i < 10; i++) {
summands.setUint32(i * 4, i, true)
}
const sum = obj.instance.exports.accumulate(0, 10)
console.log(sum)
})(module
(memory (import "js" "mem") 1)
(func (export "accumulate") (param $ptr i32) (param $len i32) (result i32)
(local $end i32)
(local $sum i32)
(local.set $end
(i32.add
(local.get $ptr)
(i32.mul
(local.get $len)
(i32.const 4))))
(block $break
(loop $top
(br_if $break
(i32.eq
(local.get $ptr)
(local.get $end)))
(local.set $sum
(i32.add
(local.get $sum)
(i32.load
(local.get $ptr))))
(local.set $ptr
(i32.add
(local.get $ptr)
(i32.const 4)))
(br $top)
)
)
(local.get $sum)
)
)你可以把它理解成:
这就是 WASM 最常见、也最工程化的价值点:计算密集 + 大数据块交互。
这一段不展开“全链路工具教程”,只告诉你每条路线的定位(选对工具比背命令更重要)。
wasm32-unknown-unknown 与 JS 互操作边界(尤其是内存与字符串)如果你只是“想先跑起来”,我的建议永远是:
先用现成 demo + instantiateStreaming 把加载、导入/导出、内存交互跑通,再决定编译链。
浏览器里的 WASM 能做很多事,但它天生缺一块:系统接口。
你不可能指望浏览器给 wasm 直接 open() 文件、connect() 网络;否则沙箱就形同虚设。
WASI(WebAssembly System Interface) 的目标就是:
Bytecode Alliance 在 2024-01-25 宣布 WASI 0.2(也被称为 WASI Preview 2 / WASIp2)正式 launched。
你可以把今天的生态理解成:
如果你正在做这些事情,WASI 值得你认真看一眼:
把 WASM 当“银弹”很容易踩坑,把它当“编译目标 + 受控能力容器”就很容易落地。
我的建议是按三步走: