更新记录
1.0.0(2026-08-20) 下载此版本
- 实现 ABI v1 的 Web 和 Android 通用 WASM 运行器。
- 增加实例加载、字节调用、释放、大小限制和统一错误码。
平台兼容性
uni-app(5.21)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| - | - | √ | - | - | - | √ | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.21)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| √ | - | √ | - | - | - |
通用 WASM 运行器(jwt-wasm)
jwt-wasm 是面向 uni-app 和 uni-app x 的 UTS WebAssembly 运行插件。它使用统一的
ABI v1,在 Web 和 Android 端加载 .wasm 二进制、调用模块方法,并以
ArrayBuffer 交换数据。
插件适合承载加密、解密、签名、验签、编解码、压缩和规则计算等纯计算逻辑。 插件本身不提供固定加密算法,也不会在 WASM 内部发起网络请求;具体能力由接入的 WASM 模块决定。
主要能力
- 从本地静态资源或 HTTPS 服务下载并加载 WASM。
- 使用统一的
method + ArrayBuffer协议调用不同业务方法。 - Web 使用浏览器原生
WebAssembly。 - Android 使用内置 Chicory 1.7.5 运行时。
- 提供实例释放、模块大小、输出大小和线性内存限制。
- 提供统一错误码,加载或执行失败不会伪装成成功。
平台兼容性
| 平台 | 支持状态 | 说明 |
|---|---|---|
| Web | 支持 | 使用浏览器原生 WebAssembly |
| Android 9+ | 支持 | 最低 API 28,使用 Chicory 1.7.5 |
| iOS | 暂不支持 | 调用时返回 9011001 |
| HarmonyOS | 暂不支持 | 调用时返回 9011001 |
| 各类小程序 | 暂不支持 | 未提供对应平台实现 |
Android 插件包含原生 JAR 依赖。使用 HBuilderX 调试 Android 时必须制作自定义基座, 标准基座无法加载插件内置的原生依赖。
安装
将插件导入项目后,确认目录存在:
uni_modules/jwt-wasm
业务代码统一从插件根目录导入,不要直接引用 utssdk 内部文件:
import {
disposeAllWasm,
disposeWasm,
invokeWasm,
isWasmSupported,
loadWasm
} from '@/uni_modules/jwt-wasm'
快速开始
1. 下载 WASM
loadWasm 接收的是 ArrayBuffer,不是 URL。先使用 uni.request 下载模块:
function downloadWasm(url, success, fail) {
uni.request({
url,
method: 'GET',
responseType: 'arraybuffer',
success: (response) => {
if (response.statusCode < 200 || response.statusCode >= 300) {
fail(new Error('WASM 服务返回状态码 ' + response.statusCode))
return
}
if (!(response.data instanceof ArrayBuffer)) {
fail(new Error('WASM 服务没有返回 ArrayBuffer'))
return
}
success(response.data)
},
fail
})
}
本地 H5 静态资源可以使用:
downloadWasm('/static/module.wasm', onSuccess, onFail)
从服务端动态加载可以使用:
downloadWasm('https://api.example.com/wasm/module.wasm', onSuccess, onFail)
服务端建议返回:
Content-Type: application/wasm
Cache-Control: public, max-age=3600
Web 端跨域下载时,服务端还需要正确配置 Access-Control-Allow-Origin。Android
正式环境应使用 HTTPS,不要通过明文 HTTP 下发可执行模块。
2. 加载并调用
let wasmHandle = ''
function runWasm(wasmBytes) {
if (!isWasmSupported()) {
console.error('当前平台不支持 WASM 运行时')
return
}
loadWasm({
bytes: wasmBytes,
success: (loaded) => {
wasmHandle = loaded.handle
const input = new TextEncoder().encode('hello wasm').buffer
invokeWasm({
handle: loaded.handle,
method: 'echo',
input,
success: (result) => {
const output = new TextDecoder().decode(result.data)
console.log(output)
},
fail: (error) => {
console.error(error.errCode, error.errMsg)
},
complete: () => {
disposeWasm(loaded.handle)
wasmHandle = ''
}
})
},
fail: (error) => {
console.error(error.errCode, error.errMsg)
}
})
}
页面退出时应释放仍在使用的实例:
onUnload() {
if (this.wasmHandle.length > 0) {
disposeWasm(this.wasmHandle)
this.wasmHandle = ''
}
}
Promise 封装
插件公开 API 使用 uni-app 标准回调形式。需要 async/await 时可以在业务层封装:
function loadWasmAsync(bytes) {
return new Promise((resolve, reject) => {
loadWasm({
bytes,
success: resolve,
fail: reject
})
})
}
function invokeWasmAsync(handle, method, input) {
return new Promise((resolve, reject) => {
invokeWasm({
handle,
method,
input,
success: (result) => resolve(result.data),
fail: reject
})
})
}
加密二进制请求示例
推荐让 WASM 负责加密和解密,使用 uni.request 负责网络传输:
业务明文 -> invokeWasm(encrypt) -> 二进制密文 -> uni.request
服务端响应 -> 二进制密文 -> invokeWasm(decrypt) -> 业务明文
直接发送二进制请求体:
invokeWasm({
handle: wasmHandle,
method: 'encrypt',
input: new TextEncoder().encode(JSON.stringify(requestData)).buffer,
success: (encrypted) => {
uni.request({
url: 'https://api.example.com/secure',
method: 'POST',
header: {
'Content-Type': 'application/octet-stream'
},
data: encrypted.data,
responseType: 'arraybuffer',
success: (response) => {
invokeWasm({
handle: wasmHandle,
method: 'decrypt',
input: response.data,
success: (decrypted) => {
const text = new TextDecoder().decode(decrypted.data)
console.log(JSON.parse(text))
}
})
}
})
}
})
抓包显示为二进制只代表请求体是字节数据,不代表 WASM 自己拥有联网能力。真正的
HTTP 请求仍由 uni.request 发出。
API
isWasmSupported()
判断当前平台是否有可用的插件运行时。
function isWasmSupported(): boolean
建议在下载模块前调用。当前 iOS 和 HarmonyOS 返回 false。
loadWasm(options)
编译、实例化并校验 ABI v1 模块。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
bytes |
ArrayBuffer |
是 | - | 完整 WASM 二进制 |
expectedAbiVersion |
number |
否 | 1 |
期望的 ABI 版本 |
maxModuleBytes |
number |
否 | 8388608 |
最大模块大小,默认 8 MiB |
maxMemoryPages |
number |
否 | 256 |
最大线性内存页数,每页 64 KiB |
success |
Function |
否 | - | 加载成功回调 |
fail |
Function |
否 | - | 加载失败回调 |
complete |
Function |
否 | - | 成功或失败后都会调用 |
成功结果:
type WasmLoadResult = {
handle: string
abiVersion: number
}
invokeWasm(options)
调用已加载实例的 ABI 入口。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
handle |
string |
是 | - | loadWasm 返回的实例句柄 |
method |
string |
是 | - | 传给 WASM 的 UTF-8 方法名 |
input |
ArrayBuffer |
是 | - | 输入字节,可以为空缓冲区 |
maxOutputBytes |
number |
否 | 8388608 |
最大输出大小,默认 8 MiB |
success |
Function |
否 | - | 调用成功回调 |
fail |
Function |
否 | - | 调用失败回调 |
complete |
Function |
否 | - | 成功或失败后都会调用 |
成功结果:
type WasmInvokeResult = {
data: ArrayBuffer
}
disposeWasm(handle)
释放指定实例。释放后继续使用该句柄会返回 9011006。
function disposeWasm(handle: string): boolean
返回 true 表示找到并释放实例,返回 false 表示句柄不存在。
disposeAllWasm()
释放插件当前持有的全部实例:
function disposeAllWasm(): void
ABI v1 接入规范
插件不是任意 WASM 文件的通用函数反射器。接入模块必须实现 ABI v1,并且不得包含 宿主导入。
模块必须导出:
int32_t wasm_abi_version(void);
int32_t wasm_alloc(int32_t size);
void wasm_free(int32_t pointer, int32_t size);
int32_t wasm_call(
int32_t method_pointer,
int32_t method_length,
int32_t input_pointer,
int32_t input_length,
int32_t output_meta_pointer
);
同时必须导出名为 memory 的线性内存。
wasm_call 参数
| 参数 | 含义 |
|---|---|
method_pointer |
UTF-8 方法名字节的起始地址 |
method_length |
方法名字节长度 |
input_pointer |
输入数据起始地址 |
input_length |
输入数据字节长度 |
output_meta_pointer |
宿主分配的 8 字节输出元数据地址 |
调用成功后,模块需要向 output_meta_pointer 写入两个小端 i32:
offset 0: output_pointer
offset 4: output_length
wasm_call 返回 0 表示成功,返回非零值会映射为 9011007。
内存所有权
- 宿主通过
wasm_alloc分别分配方法名、输入数据和输出元数据。 - WASM 必须为输出单独分配内存,不得直接把输入缓冲区作为输出返回。
- 调用结束后,宿主会对方法名、输入、元数据和输出分别调用
wasm_free。 wasm_free必须允许释放长度为 0 的逻辑数据所对应的实际分配。- 指针与长度必须位于导出的
memory范围内。
编译要求
- 不要导入 WASI、JavaScript
env函数或浏览器 API。 - 不要依赖
fetch、DOM、Node.js API 或宿主文件系统。 - C/C++、Rust、AssemblyScript 等语言都可以生成模块,但需要自行提供 ABI 适配层。
- 发布前应同时在 Web 和 Android 真机验证生成的 WASM。
资源限制
- WASM 文件默认最大 8 MiB。
- 输出数据默认最大 8 MiB。
- 线性内存默认最大 256 页,即 16 MiB。
- Android 在实例构建时强制内存上限。
- Web 会在实例化后和每次调用后检查内存大小;超限时返回
9011010并释放实例。
不要加载来源不可信的 WASM。建议服务端为模块提供固定版本号和 SHA-256,并在客户端 加载前校验摘要,避免模块被缓存污染或替换。
错误处理
失败回调接收兼容 IUniError 的错误对象:
fail: (error) => {
console.error(error.errSubject) // jwt-wasm
console.error(error.errCode)
console.error(error.errMsg)
}
| 错误码 | 含义 | 常见原因 |
|---|---|---|
9011001 |
当前平台没有 WASM 运行时 | iOS、HarmonyOS 或未实现平台 |
9011002 |
模块为空、过大或格式无效 | 传入非 WASM、下载到 HTML 错误页、超过大小限制 |
9011003 |
编译或实例化失败 | 模块损坏、指令不兼容、包含未提供的导入 |
9011004 |
缺少 ABI 必需导出 | 缺少 memory 或 ABI v1 函数 |
9011005 |
ABI 版本不匹配 | wasm_abi_version() 返回值不符合预期 |
9011006 |
实例句柄无效或已释放 | 句柄拼写错误、重复释放后继续调用 |
9011007 |
模块执行失败 | WASM 陷阱、模块返回非零状态码 |
9011008 |
内存地址或长度无效 | 模块返回越界指针或非法长度 |
9011009 |
输出超过限制 | 输出大于 maxOutputBytes |
9011010 |
资源使用超过限制 | 初始内存或运行后内存超过上限 |
常见问题
Android 提示原生依赖不能在标准基座生效
插件包含 Chicory JAR,必须重新制作并使用自定义基座。仅重新运行标准基座不能加载 新增的原生依赖。
下载 WASM 时提示只允许 HTTP 或 HTTPS
uni.request 不能读取 App 的 file:// 路径。需要将 WASM 部署到 HTTPS 服务端,
或者在业务层使用 App 文件 API 读取后再把 ArrayBuffer 传给 loadWasm。
下载成功但返回 9011002
检查响应状态码和响应内容。常见情况是 URL 实际返回了登录页面、404 HTML 或网关
JSON,而不是以 00 61 73 6d 开头的 WASM 二进制。
WASM 能不能直接调用 uni.request?
不能。ABI v1 禁止宿主导入,invokeWasm 也是同步计算调用。应先让 WASM 生成密文或
签名,再由 JS/UTS 调用 uni.request,响应回来后再次调用 WASM 解密或验签。
二进制请求是否等于加密?
不等于。application/octet-stream 只表示请求体是字节数据。是否安全取决于 WASM
模块实际采用的算法和密钥协议。生产环境建议使用 HTTPS,并采用 AES-GCM 或
ChaCha20-Poly1305 等带认证的加密方案,同时加入时间戳、随机 nonce 和防重放校验。
可以把固定密钥写进 WASM 吗?
技术上可以,但不能防止高级逆向。生产环境建议通过登录态协商会话密钥,或使用 服务端公钥和临时密钥派生方案,不要将长期固定密钥永久硬编码在客户端。
发布与生产建议
- 仅通过 HTTPS 下载 WASM 和发送业务请求。
- 为远程 WASM 使用版本化 URL,更新时同步更新摘要。
- 对 WASM 文件进行 SHA-256 或签名校验。
- 对加密消息使用认证加密,不要只使用裸流加密。
- 服务端校验时间戳、nonce 和请求唯一标识,防止重放。
- 使用完实例及时调用
disposeWasm,页面退出时处理兜底释放。 - 根据实际业务数据收紧模块、内存和输出大小限制。
- Android 发布前使用自定义基座和正式云打包分别进行真机验证。
第三方组件
Android 运行时使用 Chicory 1.7.5:
com.dylibso.chicory:runtime:1.7.5com.dylibso.chicory:wasm:1.7.5
Chicory 使用 Apache License 2.0。详细声明见 THIRD_PARTY_NOTICES.md。

收藏人数:
下载插件并导入HBuilderX
下载示例项目ZIP
赞赏(0)
下载 168
赞赏 0
下载 12520945
赞赏 1943
赞赏
京公网安备:11010802035340号