热更新失败主因是 manifest.json 的 appid 或 versionCode 不匹配:appid 必须逐字一致,versionCode 必须递增纯数字且手动更新;服务端响应字段名(update、wgtUrl)须小写、类型严格;URL 需 HTTPS 完整路径;iOS 需 plus.runtime.restart() 生效;建议增加 wgtHash 校验。

manifest.json 的 versionCode 和 appid 必须严格匹配
热更新失败最常卡在 plus.runtime.install() 报 "Invalid widget package",根本原因几乎都是 appid 或 versionCode 不一致。注意:versionName(如 "1.2.3")只用于展示,真正起校验作用的是 versionCode ——它必须是递增的纯数字(如 123),且每次打包 wgt 前必须手动加 1。
appid 更关键:HBuilderX 打包时会读取 manifest.json 中的 appid,生成的 wgt 文件名形如 UNI832D722_1.2.3.wgt;运行时 plus.runtime.install() 会用这个 appid 去解压、比对、覆盖原资源目录。错一个字符,就无法写入。
- 检查方式:在 App.vue 的
onLaunch中加一行console.log(plus.runtime.appid),和manifest.json里的appid逐字比对 - 不要用环境变量或构建脚本动态注入
appid,HBuilderX 不识别 -
versionCode改了但没生效?确认你不是只改了versionName,且打包前清除了 HBuilderX 缓存(菜单 → 工程 → 清理缓存)
服务端返回的 JSON 字段名和类型必须精确
客户端靠 uni.request() 拿到响应后,只认三个字段:update(布尔值)、wgtUrl(字符串)、pkgUrl(可选)。任何大小写错误、空值、null 或类型错位都会导致流程静默中断。
例如,服务端返回 {"Update": true, "wgturl": "https://..."} ,客户端就完全忽略;返回 {"update": "true", "wgtUrl": ""},也会跳过安装。
- 测试建议:用
curl -v "https://your-api.com/update"直接看原始响应头和 body - 确保服务端设置了
Content-Type: application/json - 字段名必须小写:
update、wgtUrl,不能是WgtUrl或wgt_url -
update字段必须是布尔类型true/false,字符串"true"会被 JS 当作真值,但部分旧版 runtime 会校验类型
plus.runtime.install() 加载前需校验文件路径与权限
调用 plus.runtime.install(wgtUrl) 后无报错但不生效,大概率是 URL 本身不可达,或下载后临时路径被系统拦截。
iOS 强制要求 HTTPS;Android 8.0+ 默认拦截 HTTP;路径含中文、空格、特殊字符(如 %20)会导致 install() 失败且不抛异常。
- URL 必须是完整、可直连的 HTTPS 地址,形如
https://cdn.example.com/wgt/UNI832D722_1.2.3.wgt - 避免使用 CDN 回源地址或带重定向的链接,
plus.runtime.install()不跟随 302 - Android targetSdkVersion ≥ 29 需适配 Scoped Storage,否则
downloadFile().tempFilePath可能无法被install()读取;建议用uni.getFileSystemManager().writeFileSync()显式落盘再传路径 - iOS 上 wgt 安装后必须调用
plus.runtime.restart()才生效,不会热替换 DOM
资源包完整性校验要靠服务端哈希 + 客户端比对
uni-app 默认不校验 wgt 包内容完整性。若服务端上传出错、CDN 缓存脏、中间网络劫持,客户端会静默安装损坏包,导致白屏或功能异常。
可靠做法是在服务端接口中额外返回 wgtHash(如 SHA-256),客户端下载完成后用 uni.getFileSystemManager().readFile() 计算本地哈希并比对。
- 示例逻辑:下载成功后,
const hash = await calcSha256(downloadResult.tempFilePath),再if (hash !== data.wgtHash) { uni.showToast({ title: '资源校验失败', icon: 'error' }); return; } - 不要依赖文件大小比对,压缩率变化会导致 size 不一致
- 哈希计算较耗时,建议在 WebWorker 中执行,避免阻塞主线程
真正容易被忽略的点是:wgt 包解压后,所有 JS 文件的模块路径、import 语句、require 调用都必须和原包结构完全一致;哪怕只是多了一个 ./ 或少了个 /,都可能因模块解析失败导致页面白屏,而控制台未必报错。











