news 2026/8/5 8:52:11

uniapp 不用发版也可以做到版本更新

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
uniapp 不用发版也可以做到版本更新

h5打开以查看

主要是针对前端发版

真的很麻烦:

  1. 改了一个 UI 样式或修复了一个小 Bug
  2. 重新打包、提审、等审核
  3. 安卓还好,iOS 动辄等几天
  4. 用户还不一定马上更新

如果是一个小 bug 却影响了大的功能,改了几行前端代码还能回退版本重新发版,却要完整走一遍应用商店发版流程,你自己想想...

以下只适合于 uniapp 的开发直接使用复制,整体的架构思想是结合现有的 uniapp 能力实现,如果这方向有兴趣,大家可以以后台可配置并开关控制前端显隐、兼容这种思维架构放在平时开发中,减少发版。

其实这个能力应该是很久之前就有了,不过利用起来使用的还是相对较少的,一部分是现在用于开发小程序的比较多,小程序得审核所以用不到,另一部分觉得发都发了直接发整版比较稳妥,可能发版也不是很经常。
很多人都不喜欢 uniapp,觉得这不行那不行,但是国内很多都在用。


一、WGT 热更新是什么?

WGT(Widget Package)是 uni-app 的App 资源升级包,本质上打包的是manifest.json、页面、JS、CSS、图片等前端资源,不包含原生引擎和原生插件的变更

对比项WGT 热更新整包更新(APK/IPA)
更新内容前端页面、样式、逻辑原生引擎、SDK、插件
是否需要上架
包体大小通常几 MB几十 MB 起
用户感知下载后重启即可需重新安装
适用场景Bug 修复、UI 调整、业务逻辑变更新增原生模块、升级 SDK

二、整体架构:后台存数据,冷启动做判断

核心思路非常简单:

┌─────────────┐ 冷启动请求 ┌──────────────┐ │ App 客户端 │ ────────────────▶ │ 版本检查接口 │ │ (uni-app) │ ◀──────────────── │ (后端 API) │ └─────────────┘ 返回版本信息 └──────┬───────┘ │ │ │ 有新版本 │ 读取 ▼ ▼ 下载 .wgt 包 ┌──────────────┐ │ │ 数据库 / 配置 │ ▼ │ version │ plus.runtime.install │ wgtUrl │ │ │ pkgUrl │ ▼ │ forceUpdate │ plus.runtime.restart └──────────────┘

流程说明:

  1. App冷启动App.vueonLaunch)时,读取当前版本号
  2. 调用后端接口,传入当前版本
  3. 后端比对数据库中的最新版本,返回是否需要更新、WGT 下载地址等
  4. 客户端下载 WGT → 安装 → 重启,新版本生效

这套方案不依赖特定后台框架,数据库、Redis、JSON 配置文件、CMS 后台都可以,只要接口能返回约定字段即可。


三、后台设计:数据库里放什么?

3.1 推荐的数据表结构

以 MySQL 为例,一张app_version表就够起步:

CREATE TABLE app_version ( id INT PRIMARY KEY AUTO_INCREMENT, app_id VARCHAR(64) NOT NULL COMMENT '应用标识,如 __UNI__XXXXXX', platform VARCHAR(16) NOT NULL DEFAULT 'all' COMMENT 'android / ios / all', version_name VARCHAR(32) NOT NULL COMMENT '版本名,如 1.0.1', version_code INT NOT NULL COMMENT '版本号,递增整数', wgt_url VARCHAR(512) DEFAULT NULL COMMENT 'WGT 包下载地址', pkg_url VARCHAR(512) DEFAULT NULL COMMENT '整包下载地址(可选)', update_type TINYINT NOT NULL DEFAULT 1 COMMENT '1=热更新 2=整包更新 3=强制整包', force_update TINYINT NOT NULL DEFAULT 0 COMMENT '是否强制更新', update_log TEXT COMMENT '更新说明', status TINYINT NOT NULL DEFAULT 1 COMMENT '1=启用 0=禁用', created_at DATETIME DEFAULT CURRENT_TIMESTAMP );

也可以不用数据库,直接在后台管理系统里维护一个 JSON 配置,原理一样:

{ "version": "1.0.2", "versionCode": 102, "update": true, "wgtUrl": "https://cdn.example.com/app/__UNI__XXXXXX.wgt", "pkgUrl": "", "forceUpdate": false, "description": "修复若干已知问题,优化首页加载速度" }

3.2 版本检查接口约定

请求参数:

参数名类型说明
nameString应用名称
versionString客户端当前版本号
platformString可选,android / ios

响应参数:

参数名类型说明
updateBoolean是否有更新
wgtUrlStringWGT 包下载地址
pkgUrlString整包下载地址(大版本升级时使用)
forceUpdateBoolean是否强制更新
descriptionString更新说明

Node.js 示例(Express):

router.get('/api/app/check-update', async (req, res) => { const { name, version, platform = 'android' } = req.query; // 从数据库查询最新启用版本 const latest = await db.query( 'SELECT * FROM app_version WHERE app_id = ? AND status = 1 ORDER BY version_code DESC LIMIT 1', [name] ); if (!latest) { return res.json({ update: false }); } const hasUpdate = compareVersion(version, latest.version_name) < 0; res.json({ update: hasUpdate, wgtUrl: hasUpdate ? latest.wgt_url : '', pkgUrl: latest.pkg_url || '', forceUpdate: !!latest.force_update, description: latest.update_log || '' }); }); // 简单版本号比较:1.0.1 vs 1.0.2 function compareVersion(v1, v2) { const a = v1.split('.').map(Number); const b = v2.split('.').map(Number); for (let i = 0; i < Math.max(a.length, b.length); i++) { const diff = (a[i] || 0) - (b[i] || 0); if (diff !== 0) return diff; } return 0; }

提示:版本比对逻辑可按业务自定义,有的团队用versionCode整数比较更稳妥。


四、制作 WGT 包:HBuilderX 两步搞定

4.1 修改版本号

打开manifest.json,递增版本信息:

  • 应用版本名称(versionName):如1.0.11.0.2
  • 应用版本号(versionCode):如101102

⚠️关键:新 WGT 包的版本号必须严格大于当前 App 已安装的版本,否则安装会报错:
WGT安装包中manifest.json文件的version版本不匹配

4.2 发行 WGT 包

HBuilderX 菜单:

发行 → App-制作应用wgt包

生成完成后,控制台会输出.wgt文件路径,文件名通常为{appid}.wgt,例如__UNI__832D722.wgt

4.3 上传到 CDN / 服务器

将 WGT 文件上传到可公网访问的 HTTPS 地址,例如:

https://cdn.example.com/app/__UNI__832D722.wgt

然后把该 URL 写入数据库或后台配置,供接口返回。


五、客户端实现:冷启动检查 + 下载安装

5.1 为什么必须用plus.runtime.getProperty

这是最容易踩的坑之一:

API读取来源WGT 更新后是否变化
plus.runtime.version原生 APK/IPA 版本❌ 不变
plus.runtime.getProperty()manifest.json 资源版本✅ 会变

结论:版本检测必须用plus.runtime.getProperty,不能用plus.runtime.version
否则 WGT 更新成功后,客户端读到的仍是原生包版本,会无限提示更新

5.2 示例(这里没有放灰度、兜底回退等代码逻辑,需要的可自行修改添加)

建议封装为独立模块,在App.vueonLaunch中调用:

// utils/appUpdate.js const CHECK_UPDATE_URL = 'https://api.example.com/api/app/check-update'; /** * 获取当前 App 资源版本信息 */ export function getCurrentVersion() { return new Promise((resolve, reject) => { // #ifdef APP-PLUS plus.runtime.getProperty(plus.runtime.appid, (info) => { resolve({ name: info.name, version: info.version, versionCode: info.versionCode }); }, reject); // #endif // #ifndef APP-PLUS reject(new Error('非 App 环境')); // #endif }); } /** * 检查并执行更新 */ export async function checkAppUpdate(options = {}) { const { silent = false } = options; try { const current = await getCurrentVersion(); const res = await uni.request({ url: CHECK_UPDATE_URL, method: 'GET', data: { name: current.name, version: current.version, platform: uni.getSystemInfoSync().platform } }); const data = res[1]?.data || res.data; if (!data?.update) return; // 整包更新(大版本 / 原生变更) if (data.pkgUrl && !data.wgtUrl) { handlePkgUpdate(data); return; } // WGT 热更新 if (data.wgtUrl) { if (data.forceUpdate) { await downloadAndInstallWgt(data.wgtUrl); } else { const confirmed = await showUpdateDialog(data.description); if (confirmed) { await downloadAndInstallWgt(data.wgtUrl); } } } } catch (err) { if (!silent) { console.error('[AppUpdate] 检查更新失败', err); } } } /** 弹出更新提示 */ function showUpdateDialog(description) { return new Promise((resolve) => { uni.showModal({ title: '发现新版本', content: description || '是否立即更新?', confirmText: '立即更新', cancelText: '稍后再说', success: (res) => resolve(res.confirm) }); }); } /** 下载并安装 WGT */ function downloadAndInstallWgt(wgtUrl) { return new Promise((resolve, reject) => { uni.showLoading({ title: '下载更新中...', mask: true }); const downloadTask = uni.downloadFile({ url: wgtUrl, success: (downloadResult) => { if (downloadResult.statusCode !== 200) { uni.hideLoading(); uni.showToast({ title: '下载失败', icon: 'none' }); return reject(new Error('下载失败')); } uni.showLoading({ title: '安装中...', mask: true }); plus.runtime.install( downloadResult.tempFilePath, { force: true }, () => { uni.hideLoading(); uni.showModal({ title: '更新完成', content: '应用将重启以生效', showCancel: false, success: () => { plus.runtime.restart(); resolve(); } }); }, (err) => { uni.hideLoading(); uni.showToast({ title: '安装失败: ' + (err.message || '未知错误'), icon: 'none' }); reject(err); } ); }, fail: (err) => { uni.hideLoading(); uni.showToast({ title: '下载失败', icon: 'none' }); reject(err); } }); // 监听下载进度(可选) downloadTask.onProgressUpdate((res) => { uni.showLoading({ title: `下载中 ${res.progress}%`, mask: true }); }); }); } /** 整包更新:跳转浏览器或应用市场 */ function handlePkgUpdate(data) { uni.showModal({ title: '发现新版本', content: data.description || '请下载安装新版本', showCancel: !data.forceUpdate, confirmText: '去下载', success: (res) => { if (res.confirm && data.pkgUrl) { plus.runtime.openURL(data.pkgUrl); } } }); }

App.vue中调用:

// App.vue import { checkAppUpdate } from '@/utils/appUpdate.js'; export default { onLaunch() { // 冷启动时静默检查更新 // #ifdef APP-PLUS checkAppUpdate({ silent: true }); // #endif } };

5.3 核心 API 说明

官方文档:plus.runtime.install

plus.runtime.install(filePath, options, successCallback, errorCallback)
  • filePath:WGT 本地路径(需先用uni.downloadFile下载到本地)
  • options.force:是否强制安装(版本不匹配时强制覆盖)
  • 安装成功后必须调用plus.runtime.restart(),新资源才会生效

六、哪些情况不能只用 WGT?

以下场景必须走整包更新

  1. 原生 SDK 变更:如新增 Maps 模块、升级推送 SDK
  2. 原生插件增改:新增或修改 uni 原生插件
  3. App 原生引擎升级
  4. 某些平台特殊限制:如从非 nvue 工程新增 nvue 且使用非自定义组件编译模式

遇到这些情况,接口应返回pkgUrl引导用户下载整包,而不是wgtUrl


七、注意事项

7.1 开发调试

  • 真机运行期间读到的 appid、版本号是 HBuilder 基座信息,必须打自定义基座或正式包才能正确测试热更新
  • 使用#ifdef APP-PLUS条件编译,避免在 H5 / 小程序环境调用plusAPI

7.2 版本管理

  • WGT 包的manifest.json版本必须大于客户端当前版本
  • 版本检测用plus.runtime.getProperty,不要用plus.runtime.version
  • 建议在后台保留历史版本记录,方便回滚

7.3 安全与合规

  • WGT 下载地址强烈建议使用 HTTPS,防止中间人篡改
  • iOS 上架审核期间不要弹出热更新提示
  • 热更新内容需符合应用商店政策,不要通过热更新绕过虚拟支付等规则
  • 参考说明:热更新是否影响应用上架

7.4 兼容性

  • WGT 资源包与原生基座存在兼容关系,大跨度升级建议在 manifest 中配置忽略不兼容提示,或先充分测试
  • 详见:wgt 与原生基座兼容性说明

7.5 用户体验

  • 非强制更新建议弹窗让用户选择,不要每次冷启动都静默强制更新
  • 下载过程展示进度,避免用户以为 App 卡死
  • 安装成功后先uni.hideLoading()plus.runtime.restart(),避免 loading 残留

八、官方升级中心:更省心的选择

如果使用 uniCloud,可以直接接入 DCloud 官方的uni-upgrade-center,开箱支持:

  • WGT 热更新 + 整包更新
  • 后台可视化管理版本
  • 多应用、多平台统一管理

官方文档:uni-upgrade-center

对于已有自建后端的团队,本文的「数据库 + 接口 + 客户端」方案更灵活;对于 uniCloud 项目,官方方案能省不少轮子。


九、总结

步骤操作
1后台 / 数据库维护版本号、WGT 下载地址
2提供版本检查接口,客户端冷启动时调用
3HBuilderX 修改版本号 → 制作 WGT 包 → 上传 CDN
4客户端getProperty读版本 → 下载 →installrestart

原来不用发版也可以做到版本更新——这句话成立的前提是:改的是前端资源,不是原生能力。在这个边界内,WGT 热更新能显著缩短从开发到用户手中的路径,特别适合高频迭代的业务型 App。

h5打开以查看

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/5 8:50:56

可视化数据图表全分类新手入门指南

在处理复杂数据时&#xff0c;我们常常面临一个尴尬的局面&#xff1a;手里握着一堆数字&#xff0c;却不知道怎么把它们变成让人一眼就能看懂的故事。很多时候&#xff0c;问题不在于数据本身不够丰富&#xff0c;而在于我们选择的表达方式不对。用错了图表&#xff0c;不仅无…

作者头像 李华
网站建设 2026/8/5 8:48:36

【免费】基于Spark实时医疗健康数据监测与疾病预测系统(Java版本+可视化大屏+Kafka+SpringBoot+Vue3) 锋哥原创出品,必属精品

大家好&#xff0c;我是Java1234_小锋老师&#xff0c;分享一套锋哥原创的基于Spark实时医疗健康数据监测与疾病预测系统(Java版本可视化大屏KafkaSpringBootVue3) 项目介绍 随着医疗信息化与物联网可穿戴设备的快速发展&#xff0c;医院与健康管理机构面临着体征数据规模大、…

作者头像 李华
网站建设 2026/8/5 8:44:37

MySQL安装与配置全指南:从入门到实践

1. MySQL安装前的准备工作 作为最流行的开源关系型数据库之一&#xff0c;MySQL在Web应用、企业系统等领域有着广泛应用。在开始安装前&#xff0c;我们需要做好以下准备工作&#xff1a; 操作系统兼容性检查 &#xff1a;MySQL支持Windows、Linux和macOS三大主流平台。以Win…

作者头像 李华
网站建设 2026/8/5 8:43:48

Cygwin环境下spdlog C++日志库的完整编译与集成指南

1. 项目概述 如果你在Windows上搞C开发&#xff0c;又习惯了Linux那套命令行工具链&#xff0c;Cygwin大概率是你的老朋友。它让你能在Windows上跑一个类Unix环境&#xff0c;用上gcc、g、make这些熟悉的工具。但当你需要引入一个像spdlog这样现代、高效的C日志库时&#xff0c…

作者头像 李华
网站建设 2026/8/5 8:39:29

Excel GetPivotData函数详解:透视表动态引用与数据查询实战

1. 从“引用”到“公式”&#xff1a;GetPivotData的定位与核心价值 如果你用过Excel的数据透视表&#xff0c;大概率遇到过这个场景&#xff1a;你建好了一个漂亮的透视表&#xff0c;想在一个单独的单元格里引用透视表里的某个汇总值&#xff0c;比如“华东区2024年1月的销售…

作者头像 李华
网站建设 2026/8/5 8:38:08

杰理AC695、AC696系列之外挂FLASH的用途

在板级配置文件中使能了flash之后&#xff0c;【fat_FLASH 配置】有三个宏定义可供选择配置&#xff0c;来选定外挂flash的用途&#xff0c;以下便是我对这三个宏定义用途的理解&#xff1a;#define TCFG_NOR_FAT ENABLE //外挂flash挂载fat文件系统&…

作者头像 李华