理解 WebAssembly:它解决什么问题

WebAssembly(简称 Wasm)是一种可在浏览器(以及越来越多其它运行时)里执行的二进制指令格式。它不是用来取代 JavaScript 写页面的,而是给「算得重、又想在 Web 里跑」的那部分逻辑一条更合适的路。

宣传语常说「近原生性能」。对前端来说,更有用的问题是:哪些工作 JS 吃力,Wasm 才值得上;以及它不解决什么。

JavaScript 与 WebAssembly 的分工

一、先说一个具体麻烦

你在浏览器里做这些事:

(1)大图滤镜、编解码
(2)物理模拟、音视频处理
(3)把已有的 C/C++/Rust 库搬到网页里用

纯 JavaScript 能写,但热路径上的数值计算、内存紧凑布局,往往更吃力;把整套原生库用 JS 重写一遍,成本又太高。

Wasm 要解决的,正是这类问题:让可移植的编译型代码,在 Web 安全模型里跑得足够快,并和 JS 互操作。

二、核心思路:给浏览器一台「小虚拟机指令集」

简单说,Wasm 像一种面向编译器的汇编约定。

你用 Rust、C/C++、Go 等语言写好模块,编译成 .wasm。浏览器加载、实例化后,JS 可以调用导出函数;Wasm 也可以经配置回调 JS。

它解决的不是「不会写 JS」,而是:

(1)性能敏感的紧循环有更可预期的执行模型
(2)复用现有原生生态,而不是全部重写
(3)在沙箱里跑,仍受 Web 安全与权限模型约束

从源码到浏览器中的 Wasm 模块

三、和 JavaScript 怎么分工

经验上较稳的切法:

(1)JS:DOM、事件、业务编排、胶水、快速迭代的产品逻辑
(2)Wasm:热点计算、编解码、密码学原语、游戏引擎核心、已有原生库的移植

两者通过导出/导入函数与线性内存交换数据。数据拷贝太勤,收益会被吃掉——这也是「上了 Wasm 反而更慢」的常见原因。

四、一条最小加载路径(概念)

现代浏览器可以用:

const { instance } = await WebAssembly.instantiateStreaming(fetch('/add.wasm'))
const result = instance.exports.add(2, 3)

上面代码中,instantiateStreaming 边下边编译;exports.add 是模块导出的函数。真实项目还有内存、打包、胶水代码(如 wasm-bindgen / Emscripten)等细节,但心智模型就是:取模块 → 实例化 → 调导出。

注意:不是所有环境都适合 instantiateStreaming(MIME 类型等要求),不支持时再退回数组缓冲再编译。

五、它不解决什么

(1)不自动让整个网站变快
瓶颈若在网络、布局、主线程长任务编排,换 Wasm 无济于事。

(2)不取代 DOM 框架
页面结构、可访问性、样式,仍是 HTML/CSS/JS 的主场。

(3)不等于无成本
编译链、调试、包体、与 JS 的边界设计都有学习与维护成本。

(4)不是「更安全所以可乱跑」
仍在沙箱中;危险的是你暴露了什么能力、处理了什么数据。

(5)不保证一定比手写优化的 JS / 内置 Web API 快
许多能力已有浏览器原生实现(如部分编解码、加密),应优先用平台 API。

六、什么时候值得认真考虑

更值得评估 Wasm 的信号:

(1)分析器显示,时间耗在纯计算热点
(2)已有成熟原生库,重写不现实
(3)需要固定内存布局的数值/媒体管道
(4)目标环境支持,且团队接得住工具链

可以先不做的信号:

(1)普通 CRUD 页面
(2)瓶颈在请求瀑布与渲染
(3)团队无人维护编译产物与 CI

七、常见误区

(1)「有了 Wasm 就可以告别 JS」
Web 应用仍需要 JS 粘合层。

(2)把大模块同步堵住首屏
注意分包、延迟加载与 Worker 卸载。

(3)频繁在 JS ↔ Wasm 拷贝大块数据
边界设计比微优化指令更重要。

(4)用 Wasm 绕开本该用 Web API 的能力
重复造编解码轮子,往往更慢更脆。

(5)忽略包体与缓存
用户下载的是二进制模块,同样占带宽。

八、小结

WebAssembly 解决的是:在 Web(及同类运行时)里,安全地运行编译型、算力密集或可复用原生代码,并与 JavaScript 协作。

它不是万能加速器,也不是新的页面标记语言。当你真有热点计算或原生库要搬进浏览器时,再把它从工具箱里拿出来。

(完)