uni-update-check
Version:
跨端(微信小程序 & App)的一体化版本更新检查工具,用于uni-app项目。支持wgt热更新、APK整包更新、iOS商店跳转以及小程序原生更新。
157 lines (142 loc) • 4.26 kB
JavaScript
;
const { compareVersion } = require('./utils');
/**
* 读取本地应用版本
* @returns {Promise<string>}
*/
function getLocalVersion() {
return new Promise((resolve) => {
try {
if (typeof plus !== 'undefined' && plus.runtime && plus.runtime.getProperty) {
plus.runtime.getProperty(plus.runtime.appid, (info) => {
resolve(info.version || '0.0.0');
});
} else {
resolve('0.0.0');
}
} catch (e) {
resolve('0.0.0');
}
});
}
/**
* 拉取服务端更新信息
* @param {string} url
* @returns {Promise<object>}
*/
function fetchServerMeta(url) {
return new Promise((resolve, reject) => {
if (!url) return resolve(null);
uni.request({
url,
method: 'GET',
success: (res) => resolve(res.data || null),
fail: reject
});
});
}
/**
* 下载并安装(支持 wgt/apk)。iOS 使用 AppStore URL 跳转。
* @param {object} meta
* @param {object} options
*/
function doUpdate(meta, options) {
const { onProgress } = options || {};
const type = String(meta.type || '').toLowerCase();
const isIOS = uni.getSystemInfoSync().platform === 'ios';
if (isIOS) {
// iOS:走 AppStore
if (meta.iosAppStoreUrl) {
plus.runtime.openURL(meta.iosAppStoreUrl);
} else {
uni.showToast({ title: '请前往 App Store 更新', icon: 'none' });
}
return;
}
if (type === 'wgt') {
if (!meta.urlWgt) {
uni.showToast({ title: '缺少 wgt 地址', icon: 'none' });
return;
}
downloadAndInstall(meta.urlWgt, 'wgt', onProgress);
} else if (type === 'apk') {
if (!meta.urlApk) {
uni.showToast({ title: '缺少 apk 地址', icon: 'none' });
return;
}
downloadAndInstall(meta.urlApk, 'apk', onProgress);
} else {
// 未知类型:优先用 wgt
if (meta.urlWgt) return downloadAndInstall(meta.urlWgt, 'wgt', onProgress);
if (meta.urlApk) return downloadAndInstall(meta.urlApk, 'apk', onProgress);
uni.showToast({ title: '未提供有效更新包', icon: 'none' });
}
}
/**
* 下载并安装
* @param {string} url
* @param {'wgt'|'apk'} pkgType
* @param {(p:number)=>void} onProgress
*/
function downloadAndInstall(url, pkgType, onProgress) {
try {
const dtask = plus.downloader.createDownload(url, {}, (d, status) => {
if (status === 200) {
plus.runtime.install(d.filename, { force: true }, () => {
plus.runtime.restart();
}, (e) => {
console.error('安装失败', e);
uni.showToast({ title: '安装失败', icon: 'none' });
});
} else {
uni.showToast({ title: '下载失败', icon: 'none' });
}
});
dtask.addEventListener('statechanged', (task, status) => {
// 3: 下载中, 4: 已完成
if (task.state === 3 && task.totalSize) {
const p = Math.floor(task.downloadedSize / task.totalSize * 100);
onProgress && onProgress(p);
} else if (task.state === 4) {
onProgress && onProgress(100);
}
});
dtask.start();
} catch (e) {
console.error('创建下载任务失败', e);
uni.showToast({ title: '创建下载任务失败', icon: 'none' });
}
}
/**
* App端检查更新入口
* @param {{url?: string, title?: string, content?: string, onProgress?: Function, confirmBeforeUpdate?: Function}} options
*/
async function checkUpdateApp(options = {}) {
const { url, title = '发现新版本', content = '是否立即更新?', confirmBeforeUpdate } = options;
const localVer = await getLocalVersion();
const meta = await fetchServerMeta(url);
if (!meta) return;
if (!compareVersion(meta.version, localVer)) {
return; // 已是最新
}
// 用户自定义确认弹窗
if (typeof confirmBeforeUpdate === 'function') {
const intercepted = await Promise.resolve(confirmBeforeUpdate(meta));
if (intercepted) return;
}
// 默认弹窗
const msg = (meta.note ? (meta.note + '\n') : '') + content;
uni.showModal({
title,
content: msg,
showCancel: meta.force ? false : true,
success: (res) => {
if (res.confirm || meta.force) {
doUpdate(meta, options);
}
}
});
}
module.exports = {
checkUpdateApp
};