强制更新需以plus.runtime.version为真实本地版本号,配合服务端严格一致的version字段比对;弹窗须路由守卫拦截、下载须监听完整生命周期、安装须适配各平台限制。

强制更新不能只靠弹窗,核心是版本比对是否可靠、触发时机是否合理、失败路径是否兜底。直接用 uni.getSystemInfoSync().version 比对会出错,iOS 和 Android 返回值行为不一致,且某些打包配置下为空;plus.runtime.version 才是真实 App 版本号,但仅在 5+ App 环境可用。
怎么拿到真正有效的本地 App 版本号
别信 uni.getSystemInfoSync().version —— 它在 iOS 返回 Bundle Version,在 Android 可能返回空或系统版本号,不是你打的包的 versionName。
- 安卓/iOS 真机必须用
plus.runtime.version(HBuilderX 打包后生效),这是 manifest.json 或 build.gradle 中定义的android:versionName/CFBundleShortVersionString - 需加 try/catch,部分低端安卓机或调试环境可能未注入 plus 对象,此时 fallback 到
uni.getSystemInfoSync().version并做非空 + 格式校验(如/^\d+\.\d+\.\d+$/) - 小程序平台无 plus,此时强制更新逻辑应跳过,或走 wgt 资源升级(
uni.getUpdateManager())
服务端返回的版本号怎么比才不会错
字符串比较 "1.10.0" 是 <code>true,但语义上错误——必须转为数字数组逐位比对。
- 用 uni-app 官方推荐的
uni.compareVersion(a, b):返回-1(a 0(相等)、1(a > b) - 服务端返回的
version字段必须和客户端格式严格一致,禁止混用"v1.2.3"、1.2.3(数字)、"1.2.3-build123" - 若服务端返回
forceUpdate: true,且uni.compareVersion(localVer, serverVer) ,才触发拦截逻辑
弹窗该在什么时候出现、怎么拦住用户
不能在 onLaunch 同步弹,也不能等用户进首页再检查——强制更新必须卡在路由就绪前。
- App 启动后用
setTimeout(() => check(), 300)延迟执行,避开 Vue 初始化和 plus 注入耗时 - 拿到服务端响应后,立即设置全局状态(如
store.commit('SET_UPDATE_BLOCK', true)),并在路由守卫中判断:若UPDATE_BLOCK为 true,则uni.redirectTo({ url: '/pages/update/force' }),该页面只渲染弹窗 + 阻止返回 - 弹窗用
uni.showModal,confirmText设为“立即更新”,cancelText设为“稍后提醒”,禁用 cancel(showCancel: false)或点击后仅关闭不跳转
下载安装阶段最容易被忽略的坑
调了 updateManager.download() 不等于更新成功,iOS 和安卓失败表现完全不同,且厂商限制极多。
- iOS 完全不支持静默安装,
plus.runtime.install()会跳转到 Safari 下载页,必须提前告知用户并引导手动安装 - 安卓上用
plus.downloader.createDownload()更可控,但需监听onHeadersReceived(确认 HTTP 200)和onDownloadComplete(确认文件完整),失败时清任务并提示“网络异常,请重试” - 华为/小米等 ROM 可能在后台杀掉下载进程,建议下载前调用
plus.runtime.setKeepAlive(true)(需权限),下载完成立即plus.runtime.restart() - APK 安装需用户开启“未知来源应用安装”权限,首次失败后应跳转系统设置页:
plus.runtime.openURL('package:com.xxx.yyy')(Android 11+ 需声明QUERY_ALL_PACKAGES)
版本比对逻辑写错、弹窗时机没卡住、下载失败没监听回调——这三个点只要漏一个,强制更新就形同虚设。尤其注意真机测试时 iOS 和安卓行为差异极大,模拟器几乎无法覆盖真实问题。











