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
  • WPF本身不支持直接的3D繪圖,但是它提供了一些用於實現3D效果的高級技術。 如果你想要在WPF中進行3D繪圖,你可以使用兩種主要的方法: WPF 3D:這是一種在WPF應用程式中創建3D圖形的方式。WPF 3D提供了一些基本的3D形狀(如立方體、球體和錐體)以及一些用於控制3D場景和對象的工具(如 ...
  • 一、XML概述 XML(可擴展標記語言)是一種用於描述數據的標記語言,旨在提供一種通用的方式來傳輸和存儲數據,特別是Web應用程式中經常使用的數據。XML並不預定義標記。因此,XML更加靈活,並且可以適用於廣泛的應用領域。 XML文檔由元素(element)、屬性(attribute)和內容(con ...
  • 從今年(2023)三月份開始,Github開始強制用戶開啟兩步驗證2FA(雙因數)登錄驗證,毫無疑問,是出於安全層面的考慮,畢竟Github賬號一旦被盜,所有代碼倉庫都會毀於一旦,關於雙因數登錄的必要性請參見:別讓你的伺服器(vps)淪為肉雞(ssh暴力破解),密鑰驗證、雙向因數登錄值得擁有。 雙因 ...
  • 第一題 下列代碼輸入什麼? public class Test { public static Test t1 = new Test(); { System.out.println("blockA"); } static { System.out.println("blockB"); } publi ...
  • 本文主要涉及的問題:用ElementTree和XPath讀寫XML文件;解決ElementTree新增元素後再寫入格式不統一的問題;QTableWidget單元格設置控制項 ...
  • QStandardItemModel 類作為標準模型,主打“類型通用”,前一篇水文中,老周還沒提到樹形結構的列表,本篇咱們就好好探討一下這貨。 還是老辦法,咱們先做示例,然後再聊知識點。下麵這個例子,使用 QTreeView 組件來顯示數據,使用的列表模型比較簡單,只有一列。 #include <Q ...
  • 一、直充內充(充值方式) 直充: 包裝套餐直接充值到上游API系統。【PID/Smart】 (如:支付寶、微信 話費/流量/語音/簡訊 等 充值系統)。 內充(套餐打包常見物聯卡系統功能): 套餐包裝 適用於不同類型套餐 如 流量、簡訊、語音 等。 (目前已完善流量邏輯) 二、套餐與計費產品 計費產 ...
  • 在前面幾天中,我們學習了Dart基礎語法、可迭代集合,它們是Flutter應用研發的基本功。今天,我們繼續學習Flutter應用另一個必須掌握知識點:非同步編程(即Future和async/await)。它類似於Java中的FutureTask、JavaScript中的Promise。它是後續Flut... ...
  • 針對改動範圍大、影響面廣的需求,我通常會問上線了最壞情況是什麼?應急預案是什麼?你帶開關了嗎?。當然開關也是有成本的,接下來本篇跟大家一起交流下高頻發佈支撐下的功能開關技術理論與實踐結合的點點滴滴。 ...
  • 1.d3.shuffle D3.shuffle() 方法用於將數組中的元素隨機排序。它使用 Fisher–Yates 洗牌演算法,該演算法是無偏的,具有最佳的漸近性能(線性時間和常數記憶體)。 D3.shuffle() 方法的語法如下: d3.shuffle(array, [start, end]) 其中 ...