uni-app 中跨端跑 C 库的 WebAssembly 实战:uasm 插件与 uni.loadUasm 完整教程

发布时间:2026/10/26 10:10:10
uni-app 中跨端跑 C 库的 WebAssembly 实战:uasm 插件与 uni.loadUasm 完整教程

uni-app 中跨端跑 C 库的 WebAssembly 实战:uasm 插件与 uni.loadUasm 完整教程

【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-app

在 uni-app 里做 zstd 压缩、crypto 加密、FFmpeg 转码这类 CPU 重活,纯 JS 写不动;可一旦换成原生库,Android 要 so、iOS 要 framework、鸿蒙又要 so,小程序和 Web 端还得再准备一套 WebAssembly。uni-app 给出的答案是 uasm(UniAssembly)插件:一份 C/C++ 库,全平台统一用uni.loadUasm()加载调用。

三个平台的原生库,为什么没法直接复用

先说清痛点。每个 App 平台其实都支持二进制库,但形态各不相同:

  • Android / 鸿蒙:.so动态库
  • iOS:framework
  • Web / 小程序:WebAssembly 编译产物

同一份 C 代码,要在五六个运行时里分别打包、分别适配调用方式。而 JS 引擎侧也有差异:uni-app x 蒸汽模式在 Android 用 V8、iOS 用 JSCore、鸿蒙用 ArkTS/JSVM,三套引擎的 C API 互不相同。

所以问题不是"能不能用二进制库",而是怎么只用一份胶水代码对接所有运行时。

原理一句话:wasm 走 Web 生态,Node-API 走 Node 生态

uasm 插件把这件事拆成了两条路(详见 uasm 插件文档):

  • Web / 小程序端:就是 WebAssembly 方案的封装,.wasm加一层胶水 JS,直接复用 Web 生态;
  • App 端:基于一套统一的 Node-API,屏蔽 V8、JSCore、JSVM 的引擎差异,把 Node 的部分能力引入了手机端。

这意味着:一个 C 库只要已经有 wasm 版本或 Node 版本封装,就能直接变成 uasm 插件;两者都没有的话,用 AI 补一层胶水也不难。

前端调用只有两个 API:uni.loadUasm()异步加载全平台可用,uni.loadUasmSync()同步加载仅限 App 平台,接口定义见 uni.loadUasm API 文档。

三步把一个 C 库变成全平台插件 🛠️

第一步:准备 wasm 与 Node-API 胶水

Web 和小程序端用 emscripten 编译,注意小程序端要加-s DYNAMIC_EXECUTION=0(微信不支持new Function这类动态行为);支付宝端还受限于 worker 中才能使用,且不支持 BigInt,遇到0n语法要用-s WASM_BIGINT=0去掉。

App 端则是写一个binding.cc,按 Node-API(napi)规范导出函数,例如add和concat两个方法。

第二步:用 uni-gyp 编译 App 端产物

App 三端不手写 CMake,而是用官方工具 uni-gyp(基于 gyp-next,扩展了 Android/iOS/HarmonyOS 的 CMake 生成能力):

npm install --save-dev @dcloudio/uni-gyp

写一个binding.gyp声明源文件和各平台条件,然后在 package.json 里配好编译命令,一条命令即可同时产出三端库并打包成 uni_module:

npm run build:test-uasm:all

注意 iOS 的 xcframework 只能在 macOS 上编译。

第三步:放入 uni_modules 并在页面加载

最终插件目录长这样(以 zstd 为例):

uni_modules/uni-zstd/uasm/ ├── web/ # 胶水 js + .wasm ├── mp-weixin/ # 胶水 js + .wasm.br ├── mp-alipay/ ├── app-android/libs/ ├── app-ios/frameworks/ └── app-harmony/libs/

页面里加载后直接调方法,返回的就是插件作者导出的方法集:

const lib = await uni.loadUasm("uni_modules/hello-uasm") lib.add(1, 2) // 3

容易踩的 3 个坑 ⚠️

  1. 小程序优先用.wasm.br:Brotli 压缩版体积更小;web 端.wasm和.wasm.br二选一,后者需要服务器支持对应的响应头。
  2. 模块要缓存,别反复加载:uni.loadUasm()的结果存起来复用即可,每次调用都重新加载会白白增加开销。
  3. 支付宝小程序的 wasm 只能在 worker 里跑,需要在workers/mp-alipay目录单独放入口文件做异步桥接,且必须真机验证。

另外,uasm 和 uts 插件可以共存于同一个 uni_modules,但定位不同:uts 面向原生能力封装(各平台代码不同),uasm 面向本来就跨平台的 C/Rust/Zig 库,JS 直通二进制、少一层桥接,性能也更高,而且真机调试不需要打自定义基座。

加载失败时先看错误码

App 端加载失败别急着重编,先按错误码定位,完整速查表在 uasm 插件文档的排查指南:

错误码含义优先检查
ERR_NATIVE_MODULE_RESOLVE_FAILED找不到模块位置插件名、打包路径
ERR_NATIVE_MODULE_LOAD_FAILED动态库加载失败so 文件、依赖库、ABI 架构
ERR_NATIVE_MODULE_ENTRY_MISSING缺少模块入口是否导出了 napi 入口函数
ERR_NATIVE_MODULE_VERSION_MISMATCHNode-API 版本不兼容模块与运行时版本

特别注意一条:把两个 so 放在同一目录不代表依赖能被找到。Failed to open native module后面跟的系统原因才是关键,not found+needed by说明是依赖库缺失,要检查依赖的 ABI 是否与应用进程一致,不能只看目录名。

适合封装成 uasm 的库都在这了

常见 C 生态里大量库已有 wasm 或 Node 版本,可以直接对接:sqlite、crypto、cmark、FFmpeg、mqtt、lame、Tesseract、Tree-sitter 等。选型时的判断标准很简单——

如果你的计算逻辑是纯 C/C++/Rust 写的、且各平台算法一致,就用 uasm;如果依赖平台原生 API(相机、地图、系统 UI),才去写 uts 插件。

下一步建议:拿你项目里最耗时的那段 JS 计算,先用 emscripten 编出 web 端 wasm 跑通uni.loadUasm()全链路,再补 App 端 uni-gyp 编译。全平台里 Web 端改动最小,是验证胶水代码正确性的最快路径。

【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-app

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考