uniapp项目实践总结(十九)版本更新和热更新实现方法

导语:当一个 APP 应用开发完成以后,就要上架应用商店,但有时候修改一些小问题或者推出一些活动,又不想频繁地提交应用商店审核,那么就可以使用应用内更新功能来进行应用的版本升级更新或热更新,下面就介绍一下实现的方法。

目录

  • 准备工作
  • 原理分析
  • 实战演练
  • 案例展示

准备工作

  • /pages/index文件夹下面新建一个version.vue的组件;
  • 按照前面文章所说的页面结构,编写好预定的页面;

原理分析

下面是应用更新的原理总结。

安装包版本更新

  • 通过uni.getSystemInfoSync方法的appVersion属性获取到应用当前安装包版本号;
  • 通过请求版本更新接口获取线上的安装包版本号;
  • 比较两个安装包版本号的大小,如果一致不更新,如果不一致,线上大于当前更新版本,线上小于当前不更新;

资源包版本更新

  • 通过uni.getStorage获取本地资源包版本号,如不存在,则通过uni.setStorage设置默认版本号;
  • 通过请求版本更新接口获取线上的资源包版本号;
  • 比较两个资源包版本号的大小,如果一致不更新,如果不一致,线上大于当前更新版本,线上小于当前不更新;

实战演练

模板使用

  • 比较版本号
html 复制代码
<view class="version-box">
  <view class="version-item">
    版本1:
    <input
      class="version-item-ipt"
      type="text"
      placeholder="请输入版本1"
      v-model="versionInfo.v1" />
  </view>
  <view class="version-item">
    版本2:
    <input
      class="version-item-ipt"
      type="text"
      placeholder="请输入版本2"
      v-model="versionInfo.v2" />
  </view>
  <view class="version-item">
    <button class="version-item-btn" type="primary" size="mini" @click="compareVersion('test')">
      比较版本
    </button>
  </view>
  <view class="version-item" v-show="versionInfo.text">
    <text>比较结果:</text>
    <text class="version-item-txt">{{ versionInfo.text }}</text>
  </view>
</view>
  • 获取线上版本
html 复制代码
<!-- #ifdef APP-PLUS -->
<view class="version-box">
  <view class="version-item">
    <button class="version-item-btn" type="primary" size="mini" @click="getVersion">
      获取版本
    </button>
  </view>
  <view class="version-item"> 当前版本: {{ checkInfo.current }} </view>
  <view class="version-item"> 线上版本: {{ checkInfo.online }} </view>
  <view class="version-item"> 当前资源包版本: {{ checkInfo.currentSource }} </view>
  <view class="version-item"> 线上资源包版本: {{ checkInfo.onlineSource }} </view>
</view>
<!-- #endif -->
  • 检测更新
html 复制代码
<!-- #ifdef APP-PLUS -->
<view class="version-box">
  <view class="version-item">
    <button class="version-item-btn" type="primary" size="mini" @click="checkUpdate">
      检测更新
    </button>
  </view>
  <view class="version-item" v-show="checkInfo.showProgress">
    <progress
      :percent="checkInfo.currentProgress"
      show-info
      :stroke-width="8"
      active-color="#24afd6" />
  </view>
</view>
<!-- #endif -->

样式编写

scss 复制代码
.version-box {
  padding: 10rpx;
  .version-item {
    display: flex;
    justify-content: flex-start;
    align-items: center;
    margin-bottom: 20rpx;
    padding: 0 10rpx;
    .version-item-ipt {
      margin-left: 20rpx;
      padding: 10rpx;
      border: 3rpx solid $e;
      font-size: 27rpx;
    }
    .version-item-btn {
      margin: 0;
    }
    .version-item-txt {
      color: $mainColor;
    }
    .uni-progress {
      width: 100%;
    }
  }
}

脚本使用

定义数据

  • 比较版本信息
js 复制代码
const versionInfo = reactive({
  v1: "", // 版本1
  v2: "", // 版本2
  text: "", // 检测结果
});
  • 检测版本信息
js 复制代码
const checkInfo = reactive({
  current: "0.0.0", // 当前版本
  online: "0.0.0", // 线上版本
  currentSource: 0, // 当前资源包
  onlineSource: 0, // 线上资源包
  result: "", // 检测结果
  data: null, // 在线信息
  type: "", // 安装类型
  showProgress: false, // 显示进度条
  currentProgress: 0, // 当前进度
  downloader: null, // 下载定时器
});

方法调用

  • 模拟版本更新数据

使用之前介绍的静态服务器放置版本更新配置文件,格式如下:

json 复制代码
{
  "install": {
    "version": "1.0.0",
    "des": "亲爱的用户:\n有新的安装包发布了\n是否更新?",
    "url": "http://192.168.1.11:3000/xxx-1.0.0.apk"
  },
  "source": {
    "version": 1,
    "des": "亲爱的用户:\n有新的资源包发布了\n是否更新?",
    "url": "http://192.168.1.11:3000/xxx-001.wgt"
  }
}
  • 比较版本号封装方法
js 复制代码
// scripts/utils.js
// 比较版本号
function compareVersion(v1, v2) {
  v1 = v1.split(".");
  v2 = v2.split(".");
  let len = Math.max(v1.length, v2.length);
  while (v1.length < len) {
    v1.push("0");
  }
  while (v2.length < len) {
    v2.push("0");
  }

  for (let i = 0; i < len; i++) {
    let num1 = parseInt(v1[i]),
      num2 = parseInt(v2[i]);

    if (num1 > num2) {
      return 1;
    } else if (num1 < num2) {
      return -1;
    }
  }

  return 0;
}
  • 比较版本操作
js 复制代码
function compareVersion() {
  if (versionInfo.v1 == "") {
    uni.showToast({
      title: "请输入版本1!",
      icon: "error",
    });
    return;
  }
  if (versionInfo.v2 == "") {
    uni.showToast({
      title: "请输入版本2!",
      icon: "error",
    });
    return;
  }
  let res = proxy.$apis.utils.compareVersion(versionInfo.v1, versionInfo.v2);
  switch (res) {
    case 0:
      versionInfo.text = "版本1和版本2相同!";
      break;
    case 1:
      versionInfo.text = "版本1比版本2要新!";
      break;
    case -1:
      versionInfo.text = "版本1比版本2要老!";
      break;
    default:
      break;
  }
}
  • 获取在线版本操作
js 复制代码
async function getVersion() {
  let system = uni.getSystemInfoSync();
  let opts = {
    url: proxy.$apis.urls.version,
    method: "get",
  };
  let data = await proxy.$http.request(opts),
    v1 = system.appVersion,
    v2 = data.install.version;
  versionInfo.v1 = v1;
  versionInfo.v2 = v2;
  checkInfo.current = v1;
  checkInfo.online = v2;
  checkInfo.data = data;
  getSource();
  console.log("请求结果:", data);
}
  • 检测更新方法
js 复制代码
function checkUpdate() {
  let result = proxy.$apis.utils.compareVersion(checkInfo.current, checkInfo.online);
  checkInfo.result = result;
  // 本地版本最新
  if (checkInfo.result == 1) {
    uni.showToast({
      title: "已是最新版本!",
      icon: "success",
    });
    return;
  }
  // 线上版本最新
  if (checkInfo.result == -1) {
    let { des, url } = checkInfo.data.install;
    checkInfo.type = "install";
    installSet(des, url);
  }
  // 本地和线上版本相同
  if (checkInfo.result == 0) {
    checkSource();
  }
}
  • 获取资源包版本
js 复制代码
//
async function getSource() {
  let { source } = checkInfo.data,
    v1 = 0,
    v2 = 0,
    local = await proxy.$apis.utils.storeage({
      type: "get",
      isSync: true,
      key: "source",
    });
  if (local.code == 1) {
    v1 = local.data;
  } else {
    proxy.$apis.utils.storeage({
      type: "set",
      isSync: true,
      key: "source",
      val: 0,
    });
    v1 = 0;
  }
  let { version } = source;
  v2 = version;
  checkInfo.currentSource = v1;
  checkInfo.onlineSource = v2;
}
  • 资源包版本检测
js 复制代码
function checkSource() {
  if (checkInfo.currentSource >= checkInfo.onlineSource) {
    uni.showToast({
      title: "已是最新版本!",
      icon: "success",
    });
    return;
  }
  let { des, url } = checkInfo.data.source;
  checkInfo.type = "source";
  installSet(des, url);
}
  • 文件安装操作
js 复制代码
function installSet(content, url) {
  uni.showModal({
    title: "系统消息",
    content,
    cancelText: "取消更新",
    cancelColor: "#333",
    confirmText: "立马更新",
    confirmColor: "#24afd6",
    success(res) {
      if (res.confirm) {
        downloadFile(url);
      }
    },
    fail() {
      uni.showToast({
        title: "更新失败!",
        icon: "error",
      });
    },
  });
}
  • 下载文件操作
js 复制代码
async function downloadFile(url) {
  checkInfo.showProgress = true;
  downloadTime();
  let opts = {
    url,
  };
  let data = await proxy.$http.download(opts);
  if (data.code === 103) {
    checkInfo.showProgress = false;
    checkInfo.currentProgress = 0;
    uni.showToast({
      title: "下载失败!",
      icon: "error",
    });
    return;
  }
  if (data) {
    checkInfo.currentProgress = 100;
    installFile(data);
  }
}
  • 下载定时器
js 复制代码
function downloadTime() {
  checkInfo.downloader = setInterval(() => {
    if (checkInfo.currentProgress < 90) {
      let randomNum = Math.ceil(Math.random() * 5);
      checkInfo.currentProgress += randomNum;
    } else {
      clearInterval(checkInfo.downloader);
    }
  }, 1000);
}
  • 安装下载的文件
js 复制代码
function installFile(file) {
  if (plus) {
    uni.showLoading({
      title: "文件安装中...",
    });
    plus.runtime.install(
      file,
      {
        force: true,
      },
      (res) => {
        uni.hideLoading();
        checkInfo.showProgress = false;
        checkInfo.currentProgress = 0;
        proxy.$apis.utils.storeage({
          type: "set",
          isSync: true,
          key: "source",
          val: checkInfo.data.source.version,
        });
        plus.runtime.restart();
      },
      (err) => {
        uni.hideLoading();
        checkInfo.showProgress = false;
        checkInfo.currentProgress = 0;
        uni.showToast({
          title: "安装失败!",
          icon: "error",
        });
      }
    );
  } else {
    uni.showToast({
      title: "此版本不支持!",
      icon: "error",
    });
  }
}

微信小程序更新

根据微信小程序官网的文档,更新方法如下:

微信小程序检测更新文档

js 复制代码
const updateManager = wx.getUpdateManager();

updateManager.onCheckForUpdate(function (res) {
  // 请求完新版本信息的回调
  console.log(res.hasUpdate);
});

updateManager.onUpdateReady(function () {
  wx.showModal({
    title: "更新提示",
    content: "新版本已经准备好,是否重启应用?",
    success: function (res) {
      if (res.confirm) {
        // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
        updateManager.applyUpdate();
      }
    },
  });
});

updateManager.onUpdateFailed(function () {
  // 新版本下载失败
});

案例展示

h5 端效果

  • 比较版本方法示例

APP 端效果

  • 已是最新版本
  • 安装包新版本更新
  • 资源包新版本更新

最后

以上就是版本更新和热更新实现方法的主要内容,有不足之处,请多多指正。

相关推荐
LYFlied16 小时前
【每日算法】LeetCode 84. 柱状图中最大的矩形
前端·算法·leetcode·面试·职场和发展
Bigger17 小时前
Tauri(21)——窗口缩放后的”失焦惊魂”,游戏控制权丢失了
前端·macos·app
Bigger17 小时前
Tauri (20)——为什么 NSPanel 窗口不能用官方 API 全屏?
前端·macos·app
bug总结17 小时前
前端开发中为什么要使用 URL().origin 提取接口根地址
开发语言·前端·javascript·vue.js·html
程序员爱钓鱼17 小时前
Node.js 编程实战:数据库连接池与性能优化
javascript·后端·node.js
老华带你飞18 小时前
建筑材料管理|基于springboot 建筑材料管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习·spring
Gomiko18 小时前
JavaScript DOM 原生部分(二):元素内容修改
开发语言·javascript·ecmascript
一招定胜负18 小时前
网络爬虫(第三部)
前端·javascript·爬虫
Data_agent18 小时前
实战:用Splash搞定JavaScript密集型网页渲染
开发语言·javascript·ecmascript
Shaneyxs18 小时前
从 0 到 1 实现CloudBase云开发 + 低代码全栈开发活动管理小程序(13)
前端