在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