WebAssembly (Wasm) 是一种可以在浏览器中高性能运行的二进制指令格式。它把 C/C++、Rust 等语言编译成接近机器码的字节码,浏览器原生执行——相当于给浏览器装了"第二语言",专治 JavaScript 跑不动的 CPU 密集任务。

为什么比 JS 快

  • JS:动态类型,运行时解释/即时编译 (JIT),有类型检查、优化、预热等开销
  • Wasm:静态类型,一次性编译成接近原生机器码,内存模型固定(线性内存),无 JIT 预热
  • 量级:CPU 密集场景通常比 JS 快数倍到数十倍,接近原生

和 JavaScript 的关系(最重要的认知)

不是替代 JS,而是分工协作:

  • Wasm 不能碰 DOM、没有 GC 堆里的对象、不能直接调浏览器 API——它只会做纯计算
  • JS 做"界面 / 事件 / IO 胶水",Wasm 做"重计算",两者通过导出函数互相调用
  • 数据交换走线性内存(一块连续字节 = ArrayBuffer),JS 侧直接读写字节;字符串/对象要先序列化或拷贝 → 这是跨边界调用的主要开销来源
  • 所以"Wasm 替代 JS"是误解;实际是 JS 负责"长得好看",Wasm 负责"算得快"

从哪来

  • 从 C/C++/Rust/Go/Zig 等编译而来,最常见工具链是 Emscripten(LLVM 后端),产出 .wasm 二进制 + 一个 .js 胶水文件
  • 也可手写 .wat(Wasm 文本格式),但很少
  • 无需安装:Chrome/Firefox/Safari/Edge 原生内置,WebAssembly 全局对象即可用

加载与调用

const { instance } = await WebAssembly.instantiateStreaming(
  fetch('engine.wasm'), { imports }
);
const result = instance.exports.compute(input);
  • instantiateStreaming 要求响应 Content-Type: application/wasm,否则降级到 ArrayBuffer
  • locateFile / wasmBinary:Emscripten 胶水里的配置,决定 wasm 从哪加载

Emscripten 产物结构

engine.wasm      // 引擎二进制(几 MB)
engine.js        // 胶水:加载 wasm、提供内存视图、包装 API、模拟文件系统
  • 胶水里常有 FS.mkdir('/pictures') —— 虚拟文件系统:把 C 的磁盘 API 模拟到内存里,JS 侧把输入文件写进去,跑"命令行式"函数,再取回输出
  • 这让"把桌面库搬进浏览器"成为可能:库内部不知道自己在浏览器里

实战观察(toolonline 的 ImageMagick)

  • lib/ImageMagick/:magickApi.js(TS 包装)+ magick.js(Emscripten 胶水)+ magick.wasm(4.2MB 完整 C++ 图像引擎)
  • 工具调用 = execute({inputFiles, commands:'convert in.png -resize 50% out.jpg'}),即在虚拟磁盘上跑 ImageMagick 命令行
  • 图片从头到尾不出本机 → 服务器零算力、隐私是卖点、离线可用
  • 逆向视角:这类工具功能全在前端,抓到 lib/ 就能完整复刻;真正的边界只在走后端算力的接口

生态案例

  • 图像:ImageMagick → wasm
  • 音视频:ffmpeg → wasm(浏览器内转码/剪辑)
  • 数据库:SQLite → wasm
  • 推理:TensorFlow Lite / llama → wasm(纯前端跑 LLM)
  • 判断标准:重计算 + 有现成 C/C++/Rust 库 → 值得 Wasm

局限 / 代价

  • 二进制体积(几 MB)有首载成本
  • 字符串/大对象跨边界拷贝有开销
  • 不能直接操作 DOM,复杂 UI 仍需 JS
  • 调试困难、二进制难读——逆向 Wasm 比逆向 JS 难一个量级,一般只逆向胶水层和调用逻辑,不逆向 wasm 本体

相关


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Web