在Uni-APP中使用WASM
我的WASM是通过RUST wasm-pack 编写的 wasm-pack 会生成 .wasm + _bg.js(胶水) + .d.ts,绑定方式也更现代(用 wasm-bindgen)
胶水文件是必须的。wasm-pack 生成的 my_wasm.js 里有 init() 函数和所有导出函数的 JS 包装,缺一不可。wasm-loader.js 的核心工作就是:先用 uni.request 把 .wasm 读成 ArrayBuffer,再把它传给胶水里的 init(arrayBuffer),绕过胶水默认的 fetch() 逻辑(小程序没有 fetch)
内存管理轻松很多:
传字符串、接收字符串 → 直接传 JS string,wasm-bindgen 自动处理 UTF-8 编解码 传数组 → 直接传 Float64Array / Int32Array,自动映射到 Rust &[f64] 唯一需要手动管理的是返回结构体,用完后必须调 statsObj.free(),否则内存泄漏
小程序的一个坑:wasm-bindgen --target web 生成的胶水里有 import.meta.url,小程序的 JS 引擎不支持。wasm-loader.js 通过直接传 ArrayBuffer 给 init() 绕过了这个问题,但如果你遇到胶水文件解析报错,可以改用 wasm-pack build --target no-modules 重新构建。
或者修改一下胶水文件
修改一下胶水文件
修改前
module_or_path = new URL('my_crypto_wasm_bg.wasm', import.meta.url);
修改后
function getWasmUrl() {
// H5 环境
// #ifdef H5
if (typeof window !== 'undefined') {
// 根据部署路径调整
return '/static/wasm/my_wasm_lib_bg.wasm';
}
// #endif
// 其他平台
return '';
}
module_or_path = getWasmUrl();
WASM加载器wasm-loader.js
/**
* wasm-loader.js
*
* 修复:UniApp/Vite 动态 import() 不支持运行时字符串路径
* 原因:Vite 在编译时对 import() 做静态分析,运行时传入变量路径会报
* "Cannot find module '/common/my_crypto_wasm'"
*
* 解决:改用静态 import 直接引入胶水文件,
* 只有 .wasm 二进制本身用平台自适应方式(fetch / uni.request)加载
*
* 用法:
* import { loadWasm } from '@/utils/wasm-loader.js'
* const wasm = await loadWasm()
* wasm.my_function(...)
*/
// ✅ 静态 import —— 路径固定,Vite/webpack 编译时能正确解析和打包
// default 导出 = init(),命名导出 = 所有 #[wasm_bindgen] 函数
import init, * as bindgenExports from '@/common/my_crypto_wasm.js'
let _initialized = false
// ─── 内部:平台自适应读取 .wasm 为 ArrayBuffer ──────────────────────────────
async function fetchWasmBuffer(url) {
/* #ifdef H5 */
const resp = await fetch(url)
if (!resp.ok) throw new Error(`[wasm-loader] fetch 失败 ${resp.status}`)
return resp.arrayBuffer()
/* #endif */
/* #ifndef H5 */
return new Promise((resolve, reject) => {
uni.request({
url,
responseType: 'arraybuffer',
success(res) {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data)
} else {
reject(new Error(`[wasm-loader] 请求失败 ${res.statusCode}`))
}
},
fail(err) {
reject(new Error(`[wasm-loader] uni.request 失败: ${JSON.stringify(err)}`))
}
})
})
/* #endif */
}
// ─── 公开 API ────────────────────────────────────────────────────────────────
/**
* 加载并初始化 WASM,返回胶水文件的所有导出
* 内部做单例缓存,多次调用只初始化一次
*
* @param {string} [wasmUrl] .wasm 文件的访问路径
* H5:相对于 public 目录的路径
* 小程序/App:完整 https:// URL 或本地路径
*/
export async function loadWasm(wasmUrl = '/static/wasm/my_crypto_wasm_bg.wasm') {
if (_initialized) return bindgenExports
const buffer = await fetchWasmBuffer(wasmUrl)
// init() 支持直接传入 ArrayBuffer,跳过胶水内部的 fetch 逻辑
await init(buffer)
_initialized = true
return bindgenExports
}
/** 重置初始化状态(用于热重载 / 单元测试) */
export function resetWasm() {
_initialized = false
}
在UNI-APP中使用
import { loadWasm } from '@/utils/wasm-loader.js'
async onLoad() {
try {
// loadWasm() 内部做了单例缓存,多个页面调用不会重复加载
this.wasm = await loadWasm()
this.ready = true
this.statusText = 'WASM 已就绪'
this.statusClass = 'ok'
} catch (e) {
this.statusText = `加载失败:${e.message}`
this.statusClass = 'err'
console.error('[WASM]', e)
}
},
doAdd() {
// wasm-bindgen 导出的函数直接像普通 JS 函数调用
this.addResult = this.wasm.add(this.numA, this.numB)
},
文档信息
版权声明:可自由转载(请注明转载出处)-非商用-非衍生
发表时间:2026年3月27日 10:02