不会 Rust 也能玩 WebAssembly:3 个 npm install 就能用的 WASM 神器

17 阅读4分钟

刷掘金热榜发现 WebAssembly 又上去了,评论区一堆人说「学 WASM 得先学 Rust」,劝退了不少人。

说实话我之前也是这么想的——直到上个月做一个内部工具的时候,发现有些 npm 包底层就是 WASM,安装完直接用,完全不需要碰 Rust。今天分享 3 个我实际用过的,都是 npm install 一把梭,零 Rust 基础也能直接上手。

先说结论

干什么的性能提升上手难度
sql.js浏览器里跑 SQLite比 IndexedDB 查询快 5-10x⭐ 极低
@ffmpeg/ffmpeg浏览器里处理视频JS 根本做不到的事⭐⭐ 低
photon-wasm图片滤镜/裁剪/压缩比 Canvas API 快 2-5x⭐ 极低

场景一:浏览器里跑 SQLite(sql.js)

做后台管理系统的时候遇到一个需求:前端要对一个几万行的 CSV 做复杂筛选和聚合。一开始用 JS 数组硬撸 filter + reduce,代码写得我自己都看不懂,而且 5 万行数据一个聚合查询要卡 3 秒。

后来想到——为什么不在浏览器里直接用 SQL?

npm install sql.js
import initSqlJs from 'sql.js';

// 初始化,需要指定 wasm 文件位置
const SQL = await initSqlJs({
  locateFile: file => `https://sql.js.org/dist/${file}`
});

// 创建内存数据库
const db = new SQL.Database();

// 建表 + 导入 CSV 数据
db.run(`CREATE TABLE sales (
  date TEXT,
  region TEXT,
  product TEXT,
  amount REAL,
  quantity INTEGER
)`);

// 批量插入(用事务,不然会巨慢)
db.run('BEGIN TRANSACTION');
csvData.forEach(row => {
  db.run(
    'INSERT INTO sales VALUES (?, ?, ?, ?, ?)',
    [row.date, row.region, row.product, row.amount, row.quantity]
  );
});
db.run('COMMIT');

// 现在可以用 SQL 了!
const result = db.exec(`
  SELECT region,
         SUM(amount) as total_sales,
         COUNT(*) as order_count,
         AVG(amount) as avg_order
  FROM sales
  WHERE date >= '2026-01-01'
  GROUP BY region
  ORDER BY total_sales DESC
`);

console.log(result[0].values);
// [['华东', 2847563.5, 12847, 221.6], ['华南', ...]]

5 万行数据,这个聚合查询 60ms 搞定。之前纯 JS 要 3 秒多。

踩坑点

  1. wasm 文件要单独加载。如果用 Vite,需要把 sql-wasm.wasm 放到 public 目录,locateFile 指向 /sql-wasm.wasm
  2. 数据库在内存里,刷新就没了。想持久化可以用 db.export() 导出 Uint8Array,存到 IndexedDB 或者 localStorage
  3. 不支持并发写入。如果有 Web Worker 也在操作同一个数据库实例,会出问题。建议把 sql.js 整个跑在一个 Worker 里
// 持久化方案
const data = db.export();
const buffer = new Uint8Array(data);
localStorage.setItem('mydb', JSON.stringify(Array.from(buffer)));

// 恢复
const saved = JSON.parse(localStorage.getItem('mydb'));
const db = new SQL.Database(new Uint8Array(saved));

场景二:浏览器里剪视频(@ffmpeg/ffmpeg)

这个是真没想到的——FFmpeg 编译成了 WASM,能在浏览器里跑。

我的场景是做一个视频剪辑小工具,用户上传视频后自动截取前 30 秒作为预览。之前都是传到后端处理,现在直接前端搞定,省了一台服务器。

npm install @ffmpeg/ffmpeg @ffmpeg/util
import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile, toBlobURL } from '@ffmpeg/util';

const ffmpeg = new FFmpeg();

// 加载 WASM(首次会比较慢,约 25MB)
const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';
await ffmpeg.load({
  coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
  wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
});

// 监听进度
ffmpeg.on('progress', ({ progress }) => {
  console.log(`处理进度: ${(progress * 100).toFixed(1)}%`);
});

// 写入文件到虚拟文件系统
const videoFile = document.querySelector('input[type="file"]').files[0];
await ffmpeg.writeFile('input.mp4', await fetchFile(videoFile));

// 截取前 30 秒 + 压缩
await ffmpeg.exec([
  '-i', 'input.mp4',
  '-t', '30',           // 只要前 30 秒
  '-vf', 'scale=720:-2', // 压缩到 720p
  '-c:v', 'libx264',
  '-preset', 'fast',
  '-crf', '28',
  'output.mp4'
]);

// 读取结果
const data = await ffmpeg.readFile('output.mp4');
const blob = new Blob([data], { type: 'video/mp4' });
const url = URL.createObjectURL(blob);

// 直接在页面上播放
document.querySelector('video').src = url;

踩坑点

  1. WASM 文件巨大。ffmpeg-core.wasm 大概 25MB,首次加载会很慢。建议做 loading 动画 + 缓存到 Service Worker
  2. SharedArrayBuffer 限制。多线程版本需要页面设置 COOP/COEP 响应头,很多部署环境不支持。单线程版也能用,就是慢一些:
    // 单线程版本,兼容性更好
    const baseURL = 'https://unpkg.com/@ffmpeg/core-st@0.12.6/dist/esm';
    
  3. 2GB 文件上限。WASM 内存限制,超过 2GB 的视频处理不了。不过前端场景一般也碰不到这个上限
  4. iOS Safari 有坑。部分老版本 Safari 对 WASM 内存分配有 bug,大文件处理可能崩溃。2026 年的 Safari 17+ 基本没问题了

场景三:图片处理快到飞起(photon-wasm)

Canvas API 做图片处理不是不能用,但一旦图片大一点(比如 4K),肉眼可见地卡。photon 是 Rust 写的图片处理库,编译成 WASM 后性能碾压 Canvas。

npm install @aspect-build/photon-wasm
# 或者直接用 CDN
import * as photon from '@aspect-build/photon-wasm';

// 从 Canvas 获取图片数据
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// 加载图片到 canvas
const img = new Image();
img.src = 'photo.jpg';
await new Promise(resolve => img.onload = resolve);
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);

// 创建 PhotonImage
const image = photon.open_image(canvas, ctx);

// 应用滤镜 —— 一行代码搞定
photon.filter(image, 'oceanic');    // 海洋风滤镜
// photon.grayscale(image);         // 灰度
// photon.gaussian_blur(image, 3);  // 高斯模糊
// photon.sharpen(image);           // 锐化

// 调整亮度对比度
photon.alter_channel(image, 0, 20);  // R通道+20

// 写回 canvas
photon.putImageData(canvas, ctx, image);

// 导出为 Blob 下载
canvas.toBlob(blob => {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'processed.jpg';
  a.click();
}, 'image/jpeg', 0.9);

实测一张 4000x3000 的照片:

操作Canvas APIphoton-wasm提速
灰度转换180ms35ms5.1x
高斯模糊2400ms480ms5.0x
批量滤镜(3个叠加)850ms190ms4.5x

踩坑点

  1. npm 包名很混乱。搜 photon wasm 会找到好几个包,认准 GitHub(silvia-odwyer/photon)上的官方版本
  2. 内存要手动管理PhotonImage 对象用完记得调用 .free() 释放 WASM 内存,不然会内存泄漏
  3. 不支持 HEIC 格式。苹果的 HEIC 图片需要先用其他库转成 JPEG/PNG 再处理

什么时候该用 WASM,什么时候别折腾

说实话大部分前端场景不需要 WASM。如果你只是做个 CRUD 后台,加什么 WASM 纯属给自己找事。

但这几种情况值得考虑:

  • 计算密集型:大量数据处理、加解密、图片/音视频处理
  • 现有 C/C++/Rust 轮子:比如 SQLite、FFmpeg,直接编译过来比用 JS 重写强一万倍
  • 需要离线能力:数据库、文档解析这些在端上跑,不依赖后端

而且 WASI 0.3 刚在今年 2 月发布了,以后 WebAssembly 不只是浏览器的东西——边缘计算、Serverless 都能用。路线图里甚至有 wasi:nn 专门用来跑 AI 推理,以后在浏览器里直接跑模型可能也不是梦。

小结

说白了 WebAssembly 对前端来说就是一个性能工具箱。不需要学 Rust,不需要懂编译原理,npm install 完就能享受 native 级别的性能。

这三个库我在实际项目里都用过,sql.js 用得最多(报表系统的前端聚合),ffmpeg.wasm 偶尔用(用户端视频预处理),photon 适合需要批量图片处理的场景。

热榜在问「前端要不要学 WASM」——我觉得不用「学」它,就行了。