背景
出于某些原因,把数据统计工作放到前端了,表格最多 20 万行,20 多列,需要计算:最大值、最小值、中位数、方差、标准差、总和,进行简单基准测试时发现,10 万个数字进行计算时,大概需要 70ms,20 个指标同时计算就需要 70 * 20 = 1400 ms,再多数据和指标就会继续上升,也就是说极限情况计算理论上可以达到 4 秒以上,不太友好
于是,正好最近在学习 tauri 和 rust,就简单写一个 npm 包,使用 WebAssembly 来运行计算功能,计算代码也不复杂就不展示了,测试下来每个指标 10万数据需要 20 ms 左右,有波动,但基本上稳定在 js 计算的 1/4 时间消耗
但是!写完之后发现,更麻烦的是该怎么在项目中接入这些包,项目比较老,用的还是 webpack4,于是就调查总结了一下,在不同构建工具中使用 wasm
wasm-pack
wasm-bindgen:rust/js底层互操作库和 cli 工具,实现rust/js类型转换,生成绑定代码wasm-pack:基于wasm-bindgen的高级构建工具链,集成了wasm-opt等优化文件大小的工具,自动化了构建、优化、打包发布到npm的完整流程
sh
# 检查版本和安装 wasm-pack
cargo -V
rustup update standable
cargo install wasm-pack
cargo install --list
# 用 wasm-pack 创建项目
# 具体功能代码略过,现在只需要打通流程,使用 demo 自带代码即可
wasm-pack new wasm-tools
# 打包成不同版本包
wasm-pack build --scope huli66 --target bundler
wasm-pack build --scope huli66 --target web
# 发布到 npm,前置条件是 npm login,手动使用 npm publish 也行
wasm-pack publish
--scope huli66 使用自己的 npm 用户名作为域,生成的 pkg 下面的 package.json 的 name 也会带上 @huli66 前缀
两种打包 target 的区别
| 特性 | bundler | web |
|---|---|---|
| 构建目标 | --target bundler | --target web |
| 加载方式 | 同步/内联 | 异步/fetch |
| 需要 init() | 不需要 | 需要 |
| 需要 MIME 配置 | 不需要 | 需要 |
| 构建工具支持 | 需要 | 不需要 |
bundler
优点 内联到代码中,不需要配置 MIME,新版本打包工具支持都比较成熟
如果作为工具包使用,体积不大,推荐 bundler 方式,发布到 npm,前端使用一般加个 loader 或者 plugin 比较方便,不一定有权限配置 nginx(卑微)
web
优点 异步加载,如果 wasm 文件体积较大,不会阻塞主线程,放在静态文件目录中,可以开启 gzip 压缩减小加载体积 不需要构建工具支持,如果是老项目使用 webpack3 这种,或者不想找版本合适的 loader plugin 等,可以使用这种方式,只需要在生产环境的 nginx 上配置 MIME 类型(新的 nginx 配置默认支持)
nginx
# mime.types,推荐修改,所有后缀为 .wasm 的文件都会自动带上返回头 Content-Type: application/wasm
types {
application/wasm wasm;
}
# 或者 default.conf 手动指定某个目录下的文件都带上指定返回头
location /static/wasm {
add_header Content-Type application/custom;
}
可能遇到的问题:
直接调用 init() 出现报错 webpack4 不支持 import meta,可以在包中找到相关代码,删掉,或者尝试手动引入 .wasm 文件然后进行使用 console 出现乱码
js
CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 3c 21 64 6f @+0

bash
开发环境没有配置 `application/wasm`
vite 中配置
webpack5 配置
webpack4 配置
所有情况
| 打包工具 | --target web | --target bundler |
|---|---|---|
| vite | 配置 MIME | vite-plugin-wasm |
| webpack5 | experiments: { asyncWebAssembly: true, syncWebAssembly: true }, | experiments: { asyncWebAssembly: true, syncWebAssembly: true }, |
| webpack4 | 配置 MIME | 不推荐,loader 已废弃 |
可以使用 @huli66/test-wasm-web @huli66/test-wasm-bundler 两个包在自己的项目中尝试一下,代码如下
- vite项目 使用 web 包需要开发环境和生产环境都配置 MIME,可以考虑把 wasm 文件放在静态服务器上,远程拉取进行 init bundler 包使用
js
// vite.config.js
import wasm from 'vite-plugin-wasm';
export default defineConfig({
plugins: [
wasm()
]
})
- webpack5 项目 最简单,配置之后两种方式都能很简单运行
json
{
"experiments": {
asyncWebAssembly: true,
syncWebAssembly: true
}
}
使用功能
js
import {calculate_statistics as calc2} from '@huli66/test-wasm-bundler';
import init, {calculate_statistics} from '@huli66/test-wasm-web'
init().then(() => {
const numbers = [1, 2, 3, 4, 5];
const result = calculate_statistics(numbers);
console.log(result, result.mean, result.median, result.standard_deviation);
});
const res2 = calc2([6, 7, 8, 9, 10]);
- webpack4 项目 推荐使用 web 包,远程引入