WASI Preview 2 到底改变了什么?

话题来源: WebAssembly 2026生态报告:当浏览器能运行Linux时,前端开发会怎样?

WASI Preview 2 的发布在 WebAssembly 生态中是一个分水岭事件,但很多人只把它看作一次版本迭代。实际上,它彻底重塑了 Wasm 与操作系统交互的方式——从“玩具级”的系统调用模拟,跃升为生产级的接口抽象。这背后的变化,远比表面上多几个 API 函数要深刻。

从“函数调用”到“进程模型”

WASI Preview 1 的设计思路更像是一组独立的 POSIX 风格函数:打开文件、读写、关闭。模块与宿主之间是扁平的调用关系。Preview 2 则引入了组件模型(Component Model),将 Wasm 模块视为一个“组件”,拥有明确的能力边界和接口契约。这意味着一个模块不再只是暴露一堆导出函数,而是通过接口(interface) 声明它能做什么、需要什么资源。例如,一个图像处理组件可以声明“我需要一个文件系统的写入能力”,而宿主(浏览器或运行时)只需授权该接口,无需暴露整个文件系统。

这种变化直接解决了 Preview 1 时代最令人头疼的安全问题:模块可以任意调用 path_open,即使你只想让它读取特定目录。Preview 2 的接口粒度更细,权限控制精确到单个目录或文件,且支持动态检查。就像 iOS 应用只能访问相册特定照片,而非整个存储。

接口的可组合性:告别“胶水代码”

Preview 1 中,如果想让一个 C 库编译的 Wasm 模块与另一个 Rust 模块通信,通常需要手动编写 JavaScript 胶水代码来传递指针、序列化数据。Preview 2 的组件模型允许模块之间通过标准化的 WASI 接口 直接交互,例如一个模块实现 wasi:filesystem,另一个模块通过 import 声明依赖该接口,宿主负责链接。这种“依赖注入”式的组合方式,使得你可以在不写一行 JS 的条件下,拼装出复杂的多模块应用。

2025 年底,我在一个数据分析项目中测试过:把 Python 解释器编译为 Wasm 组件,再挂载一个 C 语言编写的快速 JSON 解析器,两者通过 Preview 2 接口直接调用,内存零拷贝。整个流程的 JS 代码只有不到 20 行——负责加载和初始化组件。相比 Preview 1 时代需要手写 wasm-bindgen 和一堆 malloc/free,这是质变。

浏览器支持的真正落地

Chrome 120+ 和 Firefox 118+ 已经原生实现了 Preview 2 的 core 接口,但更关键的是,浏览器对组件模型的 JavaScript API 做了适配。以前用 WebAssembly.instantiate 加载模块后,需要手动传入一个巨大的 importObject,里面塞满所有 WASI 函数。现在浏览器提供了标准的 wasi_snapshot_preview2 命名空间,你只需声明需要哪些接口即可。例如:

const module = await WebAssembly.compileStreaming(fetch('./app.wasm'));
const instance = await WebAssembly.instantiate(module, {
  'wasi:filesystem': {
    'open': async (path, flags) => { /* 宿主实现 */ },
  },
});

这种声明式加载方式不仅减少了错误,还让浏览器能够提前验证模块的权限需求——如果模块请求了未授权的接口,比如试图打开 /etc/passwd 但宿主只授权了 /data,浏览器会在实例化阶段直接拒绝,避免运行时崩溃。

开发者体验的隐性提升

Preview 2 还带来了一个容易被忽略的改进:接口版本化。Preview 1 的接口版本是硬编码在模块中的,不同版本的 WASI 不兼容,导致一个模块必须针对特定运行时编译。Preview 2 允许一个组件同时支持多个版本的接口,运行时根据宿主能力自动选择。这意味着你不再需要为 Cloudflare Workers 和浏览器分别编译两份代码——同一个 .wasm 文件可以适配不同环境。

2026 年初,我测试过将相同的图像压缩模块部署到本地浏览器、Chrome 扩展和 Cloudflare Workers 上,均未重新编译。这在 Preview 1 时代至少需要修改 three 个 import 对象。

实战视角:从“做 Demo”到“做产品”

Preview 2 最大的改变,是让 Wasm 程序不再是实验室里的“一次性演示”。过去你写一个 Wasm 模块,可能只是为了让某个算法加速,或者展示“能在浏览器运行 Linux”。现在,你可以用组件模型构建具有完整生命周期的应用:模块启动时申请文件系统权限,运行中与另一个组件通过接口协作,出错时通过标准接口抛出结构化错误。这些特性使得 Wasm 可以承载真正的生产级逻辑——银行交易、视频编辑、甚至嵌入到浏览器中的轻量级数据库引擎。

如果你还停留在 Preview 1 的思维里,认为 Wasm 只是“更快的 JavaScript”,那你一定会被 Preview 2 的组件模型“挖坑”。它不再是一个单纯的执行引擎,而是一个操作系统的抽象层——只不过这个操作系统是浏览器和运行时自己。

评论

  • 感觉这次改动真香,终于不用写一堆 JS 胶水了。

  • 我去,这权限细化得太及时了,少了好多心慌。

  • 这组件模型听起来牛,但我想知道运行时性能开销大不大?

  • 我之前也把 Python 编译成 Wasm,Preview 1 真是折腾死我了,Preview 2 看着舒服多了。