翼度科技»论坛 编程开发 JavaScript 查看内容

web应用及微信小程序版本更新检测方案实践

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
背景:
  随着项目体量越来越大,用户群体越来越多,用户的声音也越来越明显;关于应用发版之后用户无感知,导致用户用的是仍然还是老版本功能,除非用户手动刷新,否则体验不到最新的功能;这样的体验非常不好,于是我们团队针对该问题给出了相应的解决方案来处理;

技术栈:vue3+ts+vite+ant-design-vue
1、web应用版本检测方案:

        1.1、基于vite开发自定义插件version-update,vite打包流程运行version-update插件在项目结构目录public文件夹下生成version.json文件,通过读取version.json文件内容与服务器上的资源文件作为版本更新的比对依据
  1. 1 /** src/plugin/versionUpdate.ts  **/
  2. 2 import fs from 'fs'
  3. 3 import path from 'path'
  4. 4 interface OptionVersion {
  5. 5   version: number | string
  6. 6 }
  7. 7 interface configObj extends Object {
  8. 8   publicDir: string
  9. 9 }
  10. 10
  11. 11 const writeVersion = (versionFileName: string, content: string | NodeJS.ArrayBufferView) => {
  12. 12   // 写入文件
  13. 13   fs.writeFile(versionFileName, content, (err) => {
  14. 14     if (err) throw err
  15. 15   })
  16. 16 }
  17. 17
  18. 18 export default (options: OptionVersion) => {
  19. 19   let config: configObj = { publicDir: '' }
  20. 20   return {
  21. 21     name: 'version-update',
  22. 22     configResolved(resolvedConfig: configObj) {
  23. 23       // 存储最终解析的配置
  24. 24       config = resolvedConfig
  25. 25     },
  26. 26
  27. 27     buildStart() {
  28. 28       // 生成版本信息文件路径
  29. 29       const file = config.publicDir + path.sep + 'version.json'
  30. 30       // 这里使用编译时间作为版本信息
  31. 31       const content = JSON.stringify({ version: options.version })
  32. 32       /** 判断目录是否存在 */
  33. 33       if (fs.existsSync(config.publicDir)) {
  34. 34         writeVersion(file, content)
  35. 35       } else {
  36. 36         /** 创建目录 */
  37. 37         fs.mkdir(config.publicDir, (err) => {
  38. 38           if (err) throw err
  39. 39           writeVersion(file, content)
  40. 40         })
  41. 41       }
  42. 42     }
  43. 43   }
  44. 44 }
复制代码
  1.2、将该文件作为插件放入到vite的plugin中去执行,并在vite构建过程中声明一个全局变量process.env.VITE__APP_VERSION__(这里仅仅只是一个变量,只用来记录打包时间和服务器json内容对比),值为当前时间戳
  1.   1 /** vite.config.ts  **/
  2.   2 import { defineConfig } from 'vite'
  3.   3 import vue from '@vitejs/plugin-vue'
  4.   4 import vueJsx from '@vitejs/plugin-vue-jsx'
  5.   5 import VueSetupExtend from 'vite-plugin-vue-setup-extend'
  6.   6 import { visualizer } from 'rollup-plugin-visualizer'
  7.   7 import viteCompression from 'vite-plugin-compression'
  8.   8 import versionUpdatePlugin from './src/plugins/versionUpdate' //Rollup 的虚拟模块
  9.   9 // vite.config.ts
  10. 10 import type { ViteSentryPluginOptions } from 'vite-plugin-sentry'
  11. 11 import viteSentry from 'vite-plugin-sentry'
  12. 12 // @ts-ignore
  13. 13 const path = require('path')
  14. 14
  15. 15 const NODE_ENV = process.env.NODE_ENV
  16. 16 const IS_PROD = NODE_ENV === 'production'
  17. 17 const CurrentTimeVersion = new Date().getTime()
  18. 18 /*
  19. 19   Configure sentry plugin
  20. 20 */
  21. 21 const sentryConfig: ViteSentryPluginOptions = {
  22. 22   url: 'https://sentry.jtexpress.com.cn',
  23. 23   authToken: '85ceca5d01ba46b2b6e92238486250d04329713adf5b4ef68a8e094102a4b6e1',
  24. 24   org: 'yl-application',
  25. 25   project: 'post-station',
  26. 26   release: process.env.npm_package_version,
  27. 27   deploy: {
  28. 28     env: 'production'
  29. 29   },
  30. 30   setCommits: {
  31. 31     auto: true
  32. 32   },
  33. 33   sourceMaps: {
  34. 34     include: ['./dist/static/js'],
  35. 35     ignore: ['node_modules'],
  36. 36     urlPrefix: '~/static/js'
  37. 37   }
  38. 38 }
  39. 39
  40. 40 // https://vitejs.dev/config/
  41. 41 export default defineConfig({
  42. 42   define: {
  43. 43     // 定义全局变量
  44. 44     'process.env.VITE__APP_VERSION__': CurrentTimeVersion
  45. 45   },
  46. 46   plugins: [
  47. 47     IS_PROD && versionUpdatePlugin({
  48. 48       version: CurrentTimeVersion
  49. 49     }),
  50. 50     vueJsx(),
  51. 51     vue(),
  52. 52     VueSetupExtend(),
  53. 53     visualizer(),
  54. 54     // gzip压缩 生产环境生成 .gz 文件
  55. 55     viteCompression({
  56. 56       verbose: true,
  57. 57       disable: false,
  58. 58       threshold: 10240,
  59. 59       algorithm: 'gzip',
  60. 60       ext: '.gz'
  61. 61     }),
  62. 62     IS_PROD && viteSentry(sentryConfig)
  63. 63   ],
  64. 64   resolve: {
  65. 65     alias: {
  66. 66       // @ts-ignore
  67. 67       '@': path.resolve(__dirname, 'src')
  68. 68     }
  69. 69   },
  70. 70   css: {
  71. 71     preprocessorOptions: {
  72. 72       less: {
  73. 73         modifyVars: {
  74. 74           // antd 自定义主题 https://www.antdv.com/docs/vue/customize-theme-cn
  75. 75           'primary-color': '#1890ff',
  76. 76           'link-color': '#1890ff',
  77. 77           'border-radius-base': '2px'
  78. 78         },
  79. 79         additionalData: '@import "@/assets/style/common.less";',
  80. 80         javascriptEnabled: true
  81. 81       }
  82. 82     }
  83. 83   },
  84. 84   server: {
  85. 85     host: '0.0.0.0',
  86. 86     port: 8080,
  87. 87     open: false,
  88. 88     https: false,
  89. 89     proxy: {}
  90. 90   },
  91. 91   build: {
  92. 92     minify: 'terser',
  93. 93     terserOptions: {
  94. 94       compress: {
  95. 95         /* 清除console */
  96. 96         drop_console: true,
  97. 97         /* 清除debugger */
  98. 98         drop_debugger: true
  99. 99       }
  100. 100     },
  101. 101     rollupOptions: {
  102. 102       output: {
  103. 103         chunkFileNames: 'static/js/[name]-[hash].js',
  104. 104         entryFileNames: 'static/js/[name]-[hash].js',
  105. 105         assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
  106. 106         manualChunks(id) {
  107. 107           //静态资源分拆打包
  108. 108           if (id.includes('node_modules')) {
  109. 109             return id.toString().split('node_modules/')[1].split('/')[0].toString()
  110. 110           }
  111. 111         }
  112. 112       }
  113. 113     },
  114. 114     sourcemap: IS_PROD
  115. 115   }
  116. 116 })
复制代码
  1.3、封装全局方法,请求当前域名下的/version.json资源,将vite中定义的全局变量和当前域名下的json文件内容做对比,如果不一致,我们则认为发布了新版本
  1. 1 /** src/utils/checkVersion.ts **/
  2. 2 import { Modal } from 'ant-design-vue'
  3. 3 export default function versionCheck() {
  4. 4   const timestamp = new Date().getTime()
  5. 5   //import.meta.env.MODE 获取的是开发还是生产版本的
  6. 6   if (import.meta.env.MODE === 'development') return
  7. 7
  8. 8   fetch(`/version.json?t=${timestamp}`)
  9. 9     .then((res) => {
  10. 10       return res.json()
  11. 11     })
  12. 12     .then((response) => {
  13. 13       if (process.env.VITE__APP_VERSION__ !== response.version) {
  14. 14         Modal.confirm({
  15. 15           title: '发现新版本',
  16. 16           content: '检测到最新版本,刷新后立即使用...',
  17. 17           okText: '立即更新',
  18. 18           cancelText: '暂不更新',
  19. 19           onOk() {
  20. 20             window.location.reload()
  21. 21           }
  22. 22         })
  23. 23       }
  24. 24     })
  25. 25 }
复制代码
        1.4 、在layout全局文件中,监听路由变化,去调版本检测的方法来决定是否弹出提醒(尊重用户的选择,是否更新页面由用户来决定)
  1. 1 /** src/layout/index.vue **/
  2. 2  watch(
  3. 3     () => router.currentRoute.value,
  4. 4     (newValue: any) => {
  5. 5       /** 判断是否有更高的版本 */
  6. 6       checkVersion()
  7. 7     },
  8. 8     { immediate: true }
  9. 9   )
复制代码
 
2、微信小程序应用版本检测方案:
 流程图:

  技术栈:Taro-vue+ts+nut-ui (京东物流风格轻量移动端组件库)
   2.1、基于微信开发文档提供的getUpdateManager版本更新管理器api去实现版本更新的检测与新版本应用的下载,封装全局方法
  1. 1 /** 检查小程序版本更新 */
  2. 2 export function checkMiniProgramVersion() {
  3. 3   if (Taro.canIUse('getUpdateManager')) {
  4. 4     const updateManager = Taro.getUpdateManager();
  5. 5     updateManager.onCheckForUpdate(function (res) {
  6. 6       // 请求完新版本信息的回调
  7. 7       if (res.hasUpdate) {
  8. 8         Taro.showModal({
  9. 9           title: '新版本更新提示',
  10. 10           content: '检测到新版本,是否下载新版本并重启小程序?',
  11. 11           success: function (res) {
  12. 12             if (res.confirm) {
  13. 13               downloadAndUpdate(updateManager);
  14. 14             }
  15. 15           },
  16. 16         });
  17. 17       }
  18. 18     });
  19. 19   } else {
  20. 20     Taro.showModal({
  21. 21       title: '新版本更新提示',
  22. 22       showCancel: false,
  23. 23       content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。',
  24. 24       success: function () {
  25. 25         Taro.exitMiniProgram();
  26. 26       },
  27. 27     });
  28. 28   }
  29. 29 }
  30. 30
  31. 31 /** 新版本应用下载 */
  32. 32 export function downloadAndUpdate(updateManager) {
  33. 33   Taro.showLoading();
  34. 34   updateManager.onUpdateReady(function () {
  35. 35     Taro.hideLoading();
  36. 36     Taro.showModal({
  37. 37       title: '新版本更新提示',
  38. 38       content: '新版本已经准备好,是否重启应用?',
  39. 39       success: function (res) {
  40. 40         if (res.confirm) {
  41. 41           // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
  42. 42           updateManager.applyUpdate();
  43. 43         }
  44. 44       },
  45. 45     });
  46. 46   });
  47. 47
  48. 48   updateManager.onUpdateFailed(function () {
  49. 49     Taro.showLoading();
  50. 50     Taro.showModal({
  51. 51       title: '新版本更新提示',
  52. 52       showCancel: false,
  53. 53       content: '新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~',
  54. 54       success: function () {
  55. 55         // 退出应用
  56. 56         Taro.exitMiniProgram();
  57. 57       },
  58. 58     });
  59. 59   });
  60. 60 }
复制代码
  2.2、在app.ts入口文件onLaunch的生命周期钩子函数中去调用方法检测版本
  1. 1 // src/app.ts
  2. 2 import { createApp } from 'vue';
  3. 3 import Taro from '@tarojs/taro';
  4. 4 import './app.scss';
  5. 5 import store from './store';
  6. 6 import installPlugin from '@/plugins/index';
  7. 7 import { wxLogin, checkMiniProgramVersion } from '@/utils/common';
  8. 8
  9. 9 const App = createApp({
  10. 10   onLaunch() {
  11. 11     /** 检测小程序版本 */
  12. 12     checkMiniProgramVersion();
  13. 13   },
  14. 14 });
  15. 15
  16. 16 App.use(store);
  17. 17 installPlugin(App);
  18. 18
  19. 19 export default App;
复制代码
 坑点Tips:
  小程序更新方案中由于受限于小程序的更新机制,第一次发版无效,因为微信小程序服务器上的版本中不存在checkMiniProgramVersion方法,所以无法执行;更新提示弹窗需要在第二次发版时才生效;
出处:https://www.cnblogs.com/dengyao-blogs/本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
来源:https://www.cnblogs.com/dengyao-blogs/p/17708054.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具