web應用及微信小程式版本更新檢測方案實踐

来源:https://www.cnblogs.com/dengyao-blogs/archive/2023/09/17/17708054.html
-Advertisement-
Play Games

背景: 隨著項目體量越來越大,用戶群體越來越多,用戶的聲音也越來越明顯;關於應用發版之後用戶無感知,導致用戶用的是仍然還是老版本功能,除非用戶手動刷新,否則體驗不到最新的功能;這樣的體驗非常不好,於是我們團隊針對該問題給出了相應的解決方案來處理;技術棧:vue3+ts+vite+ant-design ...


背景:

  隨著項目體量越來越大,用戶群體越來越多,用戶的聲音也越來越明顯;關於應用發版之後用戶無感知,導致用戶用的是仍然還是老版本功能,除非用戶手動刷新,否則體驗不到最新的功能;這樣的體驗非常不好,於是我們團隊針對該問題給出了相應的解決方案來處理;

技術棧:vue3+ts+vite+ant-design-vue

1、web應用版本檢測方案:

        1.1、基於vite開發自定義插件version-update,vite打包流程運行version-update插件在項目結構目錄public文件夾下生成version.json文件,通過讀取version.json文件內容與伺服器上的資源文件作為版本更新的比對依據

 1 /** src/plugin/versionUpdate.ts  **/
 2 import fs from 'fs'
 3 import path from 'path'
 4 interface OptionVersion {
 5   version: number | string
 6 }
 7 interface configObj extends Object {
 8   publicDir: string
 9 }
10 
11 const writeVersion = (versionFileName: string, content: string | NodeJS.ArrayBufferView) => {
12   // 寫入文件
13   fs.writeFile(versionFileName, content, (err) => {
14     if (err) throw err
15   })
16 }
17 
18 export default (options: OptionVersion) => {
19   let config: configObj = { publicDir: '' }
20   return {
21     name: 'version-update',
22     configResolved(resolvedConfig: configObj) {
23       // 存儲最終解析的配置
24       config = resolvedConfig
25     },
26 
27     buildStart() {
28       // 生成版本信息文件路徑
29       const file = config.publicDir + path.sep + 'version.json'
30       // 這裡使用編譯時間作為版本信息
31       const content = JSON.stringify({ version: options.version })
32       /** 判斷目錄是否存在 */
33       if (fs.existsSync(config.publicDir)) {
34         writeVersion(file, content)
35       } else {
36         /** 創建目錄 */
37         fs.mkdir(config.publicDir, (err) => {
38           if (err) throw err
39           writeVersion(file, content)
40         })
41       }
42     }
43   }
44 }

  1.2、將該文件作為插件放入到vite的plugin中去執行,併在vite構建過程中聲明一個全局變數process.env.VITE__APP_VERSION__(這裡僅僅只是一個變數,只用來記錄打包時間和伺服器json內容對比),值為當前時間戳

  1 /** vite.config.ts  **/
  2 import { defineConfig } from 'vite'
  3 import vue from '@vitejs/plugin-vue'
  4 import vueJsx from '@vitejs/plugin-vue-jsx'
  5 import VueSetupExtend from 'vite-plugin-vue-setup-extend'
  6 import { visualizer } from 'rollup-plugin-visualizer'
  7 import viteCompression from 'vite-plugin-compression'
  8 import versionUpdatePlugin from './src/plugins/versionUpdate' //Rollup 的虛擬模塊
  9 // vite.config.ts
 10 import type { ViteSentryPluginOptions } from 'vite-plugin-sentry'
 11 import viteSentry from 'vite-plugin-sentry'
 12 // @ts-ignore
 13 const path = require('path')
 14 
 15 const NODE_ENV = process.env.NODE_ENV
 16 const IS_PROD = NODE_ENV === 'production'
 17 const CurrentTimeVersion = new Date().getTime()
 18 /*
 19   Configure sentry plugin
 20 */
 21 const sentryConfig: ViteSentryPluginOptions = {
 22   url: 'https://sentry.jtexpress.com.cn',
 23   authToken: '85ceca5d01ba46b2b6e92238486250d04329713adf5b4ef68a8e094102a4b6e1',
 24   org: 'yl-application',
 25   project: 'post-station',
 26   release: process.env.npm_package_version,
 27   deploy: {
 28     env: 'production'
 29   },
 30   setCommits: {
 31     auto: true
 32   },
 33   sourceMaps: {
 34     include: ['./dist/static/js'],
 35     ignore: ['node_modules'],
 36     urlPrefix: '~/static/js'
 37   }
 38 }
 39 
 40 // https://vitejs.dev/config/
 41 export default defineConfig({
 42   define: {
 43     // 定義全局變數
 44     'process.env.VITE__APP_VERSION__': CurrentTimeVersion
 45   },
 46   plugins: [
 47     IS_PROD && versionUpdatePlugin({
 48       version: CurrentTimeVersion
 49     }),
 50     vueJsx(),
 51     vue(),
 52     VueSetupExtend(),
 53     visualizer(),
 54     // gzip壓縮 生產環境生成 .gz 文件
 55     viteCompression({
 56       verbose: true,
 57       disable: false,
 58       threshold: 10240,
 59       algorithm: 'gzip',
 60       ext: '.gz'
 61     }),
 62     IS_PROD && viteSentry(sentryConfig)
 63   ],
 64   resolve: {
 65     alias: {
 66       // @ts-ignore
 67       '@': path.resolve(__dirname, 'src')
 68     }
 69   },
 70   css: {
 71     preprocessorOptions: {
 72       less: {
 73         modifyVars: {
 74           // antd 自定義主題 https://www.antdv.com/docs/vue/customize-theme-cn
 75           'primary-color': '#1890ff',
 76           'link-color': '#1890ff',
 77           'border-radius-base': '2px'
 78         },
 79         additionalData: '@import "@/assets/style/common.less";',
 80         javascriptEnabled: true
 81       }
 82     }
 83   },
 84   server: {
 85     host: '0.0.0.0',
 86     port: 8080,
 87     open: false,
 88     https: false,
 89     proxy: {}
 90   },
 91   build: {
 92     minify: 'terser',
 93     terserOptions: {
 94       compress: {
 95         /* 清除console */
 96         drop_console: true,
 97         /* 清除debugger */
 98         drop_debugger: true
 99       }
100     },
101     rollupOptions: {
102       output: {
103         chunkFileNames: 'static/js/[name]-[hash].js',
104         entryFileNames: 'static/js/[name]-[hash].js',
105         assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
106         manualChunks(id) {
107           //靜態資源分拆打包
108           if (id.includes('node_modules')) {
109             return id.toString().split('node_modules/')[1].split('/')[0].toString()
110           }
111         }
112       }
113     },
114     sourcemap: IS_PROD
115   }
116 })

  1.3、封裝全局方法,請求當前功能變數名稱下的/version.json資源,將vite中定義的全局變數和當前功能變數名稱下的json文件內容做對比,如果不一致,我們則認為發佈了新版本

 1 /** src/utils/checkVersion.ts **/
 2 import { Modal } from 'ant-design-vue'
 3 export default function versionCheck() {
 4   const timestamp = new Date().getTime()
 5   //import.meta.env.MODE 獲取的是開發還是生產版本的
 6   if (import.meta.env.MODE === 'development') return
 7 
 8   fetch(`/version.json?t=${timestamp}`)
 9     .then((res) => {
10       return res.json()
11     })
12     .then((response) => {
13       if (process.env.VITE__APP_VERSION__ !== response.version) {
14         Modal.confirm({
15           title: '發現新版本',
16           content: '檢測到最新版本,刷新後立即使用...',
17           okText: '立即更新',
18           cancelText: '暫不更新',
19           onOk() {
20             window.location.reload()
21           }
22         })
23       }
24     })
25 }

        1.4 、在layout全局文件中,監聽路由變化,去調版本檢測的方法來決定是否彈出提醒(尊重用戶的選擇,是否更新頁面由用戶來決定)

1 /** src/layout/index.vue **/
2  watch(
3     () => router.currentRoute.value,
4     (newValue: any) => {
5       /** 判斷是否有更高的版本 */
6       checkVersion()
7     },
8     { immediate: true }
9   )

 

2、微信小程式應用版本檢測方案:

 流程圖:

  技術棧:Taro-vue+ts+nut-ui (京東物流風格輕量移動端組件庫)

   2.1、基於微信開發文檔提供的getUpdateManager版本更新管理器api去實現版本更新的檢測與新版本應用的下載,封裝全局方法

 1 /** 檢查小程式版本更新 */
 2 export function checkMiniProgramVersion() {
 3   if (Taro.canIUse('getUpdateManager')) {
 4     const updateManager = Taro.getUpdateManager();
 5     updateManager.onCheckForUpdate(function (res) {
 6       // 請求完新版本信息的回調
 7       if (res.hasUpdate) {
 8         Taro.showModal({
 9           title: '新版本更新提示',
10           content: '檢測到新版本,是否下載新版本並重啟小程式?',
11           success: function (res) {
12             if (res.confirm) {
13               downloadAndUpdate(updateManager);
14             }
15           },
16         });
17       }
18     });
19   } else {
20     Taro.showModal({
21       title: '新版本更新提示',
22       showCancel: false,
23       content: '當前微信版本過低,無法使用該功能,請升級到最新微信版本後重試。',
24       success: function () {
25         Taro.exitMiniProgram();
26       },
27     });
28   }
29 }
30 
31 /** 新版本應用下載 */
32 export function downloadAndUpdate(updateManager) {
33   Taro.showLoading();
34   updateManager.onUpdateReady(function () {
35     Taro.hideLoading();
36     Taro.showModal({
37       title: '新版本更新提示',
38       content: '新版本已經準備好,是否重啟應用?',
39       success: function (res) {
40         if (res.confirm) {
41           // 新的版本已經下載好,調用 applyUpdate 應用新版本並重啟
42           updateManager.applyUpdate();
43         }
44       },
45     });
46   });
47 
48   updateManager.onUpdateFailed(function () {
49     Taro.showLoading();
50     Taro.showModal({
51       title: '新版本更新提示',
52       showCancel: false,
53       content: '新版本已經上線啦~,請您刪除當前小程式,重新搜索打開喲~',
54       success: function () {
55         // 退出應用
56         Taro.exitMiniProgram();
57       },
58     });
59   });
60 }

  2.2、在app.ts入口文件onLaunch的生命周期鉤子函數中去調用方法檢測版本

 1 // src/app.ts
 2 import { createApp } from 'vue';
 3 import Taro from '@tarojs/taro';
 4 import './app.scss';
 5 import store from './store';
 6 import installPlugin from '@/plugins/index';
 7 import { wxLogin, checkMiniProgramVersion } from '@/utils/common';
 8 
 9 const App = createApp({
10   onLaunch() {
11     /** 檢測小程式版本 */
12     checkMiniProgramVersion();
13   },
14 });
15 
16 App.use(store);
17 installPlugin(App);
18 
19 export default App;

 坑點Tips:

  小程式更新方案中由於受限於小程式的更新機制,第一次發版無效,因為微信小程式伺服器上的版本中不存在checkMiniProgramVersion方法,所以無法執行;更新提示彈窗需要在第二次發版時才生效;

作者:有夢想的鹹魚前端 出處:https://www.cnblogs.com/dengyao-blogs/ 本文版權歸作者和博客園共有,歡迎轉載,但必須給出原文鏈接,並保留此段聲明,否則保留追究法律責任的權利。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 古時候寫代碼,許可權這塊寫過一個庫,基本就是一個泛型介面,裡面有幾個方法: 如驗證輸入的principal和credentials,返回token和authorities和roles,role就是一堆authorities集,也就說就是返回一堆authorities。然後每次請求會拿token找到au ...
  • 在設備維修保養管理中,及時通知相關人員是確保設備得到及時維護的關鍵。API介面提供了一個方便的方式來自動發送維修保養通知,以確保工作流程的順利進行。本文將詳細介紹如何使用成熟的API介面來發送設備維修保養通知,以確保設備得到及時的維護,同時提供通俗易懂的步驟和代碼說明。 什麼是API介面? 首先,讓 ...
  • 一、文件管理: 1.1、ls: ​ 顯示文件/目錄屬性 常見參數: -l :列出長數據串,包含文件的屬性與許可權數據等 -a :列出全部的文件,連同隱藏文件(開頭為.的文件)一起列出來(常用) -d :僅列出目錄本身,而不是列出目錄的文件數據 -h :將文件容量以較易讀的方式(GB,kB等)列出來 - ...
  • 1.date文件的備份 2.mysqldump 備份 說明:mysqldump是MySQL資料庫中的一個實用程式,它主要用於轉儲(備份)資料庫。mysqldump通過生成一個SQL腳本文件,包含從頭開始重新創建資料庫所必需的(如 CREATE TABLE和INSERT等),來實現資料庫的備份和轉儲。 ...
  • 1.分組 group by 詳情見,發佈的第七篇博客文章,7- MySQL函數 2.排序 order by 說明:在MySQL中,ORDER BY是一種用於對查詢結果進行排序的關鍵字。它可以根據一列或多列的值,以升序或降序的方式對查詢結果進行排序,使得查詢者可以更加方便 地查看、分析和處理數據。 使 ...
  • 1.分組group by 在MySQL中,GROUP BY的意思是“分組查詢”,它可以根據一個或多個欄位對查詢結果進行分組。 GROUP BY的作用是通過一定的規則將一個數據集劃分成若幹個小的區域,然後針對若幹個小區域進行數據處理。這可以理解為將數據按照某個欄位或者多個欄位進行分組。 使用GROUP ...
  • MySQL資料庫管理 資料庫-->數據表-->行(記錄):用來描述一個對象的信息 列(欄位):用來描述對象的一個屬性 常用的數據類型: int :整型 無符號[0,2^32-1],有符號[-2^31,2^31-1] float :單精度浮點 4位元組32位 double :雙精度浮點 8位元組64位 c ...
  • 在MySQL中,高級查詢是指使用更複雜的查詢語句和操作符來檢索和操作資料庫中的數據。高級查詢可以幫助您更精確地找到所需的信息,並提高查詢的效率和靈活性。 以下是高級查詢的一些常見應用場景和意義: 連接多個表:使用JOIN操作符將多個表連接起來,以便在一次查詢中獲取相關聯的數據。這對於在多個表之間建立 ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...