热更新必须满足versioncode严格递增、appid完全一致,校验wgt包sha256完整性,备份旧版资源并记录versioncode,回滚需还原资源+降级versioncode+重启,服务端响应字段和类型须精确匹配。

manifest.json 版本号与 versionCode 必须严格递增
热更新触发的前提不是“看起来版本变了”,而是 versionCode(纯数字)必须比当前运行 App 的 versionCode 大,且 appid 完全一致。很多开发者改了 versionName(如 "1.0.1"),但没动 versionCode(仍为 100),导致 plus.runtime.install() 直接跳过安装逻辑。
-
versionName仅用于展示,不影响热更新判定 -
versionCode必须是整数,每次 wgt 包升级都需 +1(如从 101 → 102) -
appid错一位(比如 UNI832D722 写成 UNI832D723)会导致Invalid widget package错误,且无明确日志提示 - HBuilderX 打包 wgt 时会自动读取
manifest.json中的appid和versionName,生成文件名如UNI832D722_1.0.1.wgt,但校验依赖的是运行时plus.runtime.getProperty()返回的versionCode
下载前必须校验 wgt 包完整性(SHA256)
增量更新最怕“半截包”:网络中断、CDN 缓存污染、服务器返回 200 但内容为空,都会让 plus.runtime.install() 静默失败或覆盖出错。不能只靠 HTTP 状态码判断成功。
- 服务端需为每个 wgt 包提供同名
.sha256文件(如UNI832D722_1.0.1.wgt.sha256),内容为该 wgt 的 SHA256 值 - 客户端下载 wgt 后,用
uni.getFileSystemManager().readFile()读取临时文件,再调用uni.getSystemInfoSync().platform === 'ios' ? crypto.subtle.digest() : plus.io.resolveLocalFileSystemURL()计算 SHA256 并比对 - 校验失败必须主动 reject,避免调用
plus.runtime.install();否则可能损坏资源目录,导致白屏 - Android 上若启用 Scoped Storage(targetSdkVersion ≥ 29),
tempFilePath可能无法直接读取,需先用plus.io.convertLocalFileSystemURL()转换路径
回滚依赖本地备份 + manifest.json 版本快照
uni-app 没有内置回滚 API,所谓“回滚”本质是手动还原上一版 wgt 资源 + 降级 versionCode。关键在于:你得提前把旧版资源存下来,且知道它对应哪个 versionCode。
- 首次安装或每次成功 install 后,用
plus.io.copyTo()将当前__UNI__xxx/www/目录完整备份到_backup_v101/这类可读路径(iOS 需写入Library/Caches,Android 推荐getExternalFilesDir()) - 备份同时,把当时生效的
versionCode和wgtUrl记录到本地 storage(如uni.setStorageSync('rollback_meta', { v: 101, url: 'https://...' })) - 回滚时,先删掉当前
www/目录,再用plus.io.copyTo()把_backup_v101/恢复过去,最后调用plus.runtime.restart() - 注意:iOS 上
plus.runtime.restart()不会立即刷新界面,需等下次冷启动才生效;Android 则基本即时 —— 回滚后务必提示用户“重启生效”
update 接口响应结构必须精确匹配
客户端靠 res.data.update 和 res.data.wgtUrl 两个字段驱动流程,任何拼写、类型或空值错误都会中断整个链路,且不报错。
- 服务端返回必须是
Content-Type: application/json,否则uni.request()的res.data会是字符串而非对象 -
update字段必须为布尔值true或false,不能是字符串"true"或数字1 -
wgtUrl必须是非空字符串(""、null、undefined都会导致plus.runtime.install()报错Invalid argument) - 建议加一层防御性判断:
if (res.data && res.data.update === true && typeof res.data.wgtUrl === 'string' && res.data.wgtUrl.trim()) { ... } - 测试时用
curl -H "Accept: application/json" "https://your-api.com/update?version=101"直接看原始响应,绕过前端 JS 干扰











