前端開發(fā)記錄:自動檢測更新

使用Webpack、Vite等構(gòu)建工具打包時,會為靜態(tài)資源添加hash,確保每次更新都能生成唯一的文件名。然而,當新版本部署后,用戶瀏覽器中可能仍然緩存著舊版本的文件。雖然可以設(shè)置較短的緩存時間,但用戶仍然需要手動刷新頁面才能獲取最新版本。那么如何讓用戶在頁面打開期間,自動感知到新版本的發(fā)布并完成更新?

下面是一個簡易的輪詢檢測script資源變化來實現(xiàn)自動更新提醒:

<script setup name="">
// 常量定義
const POLL_INTERVAL = 2000
let cachedScriptSources = null // 緩存的script地址
const SCRIPT_SRC_REGEX = /<script[^>]*src=["']([^"']+)/g

// 獲取頁面中所有script的src地址
async function fetchScriptSources() {
  try {
    // Date.now() 用于防止瀏覽器緩存
    const html = await fetch(`/?timestamp=${Date.now()}`).then((res) => res.text())
    const sources = []
    let match

    SCRIPT_SRC_REGEX.lastIndex = 0 // 重置正則表達式狀態(tài)
    while((match = SCRIPT_SRC_REGEX.exec(html))) {
      sources.push(match[1])
    }

    return sources
  } catch (error) {
    console.error('獲取頁面資源失敗:', error)
    return cachedScriptSources || []
  }
}

// 檢查頁面是否需要更新
async function checkForUpdates() {
  const newScriptSources = await fetchScriptSources()

  if (!cachedScriptSources) {
    cachedScriptSources = newScriptSources
    return false
  }

  // 比較兩個數(shù)組是否相同
  if (cachedScriptSources.length !== newScriptSources.length) {
    cachedScriptSources = newScriptSources
    return true
  }

  const hasChanges = cachedScriptSources.some((src, index) => src !== newScriptSources[index])
  cachedScriptSources = newScriptSources

  return hasChanges
}

// 輪詢檢查頁面更新
function startUpdatePolling() {
  const poll = async () => {
    const shouldUpdate = await checkForUpdates()

    if (shouldUpdate) {
      const userConfirmed = confirm('檢測到頁面有更新,是否刷新頁面?')
      if (userConfirmed) {
        location.reload()
        return // 刷新頁面,不需要繼續(xù)輪詢
      }
    }

    setTimeout(poll, POLL_INTERVAL)
  }

  setTimeout(poll, POLL_INTERVAL)
}

startUpdatePolling()
</script>
原理詳解
1、監(jiān)控資源文件變化

現(xiàn)代前端應用構(gòu)建后,通常會給靜態(tài)資源(如JavaScript、CSS文件)添加hash值作為版本標識:

app.js → app.abc123.js (版本1)
app.js → app.def456.js (版本2,內(nèi)容更新后hash變化)

通過檢測這些資源文件名的變化來判斷是否有新版本發(fā)布。

2、繞過瀏覽器緩存
fetch(`/?timestamp=${Date.now()}`)

這里使用時間戳作為查詢參數(shù),確保每次請求都能獲取最新的HTML文件,而不是瀏覽器緩存的舊版本。

3、提取Script資源
const SCRIPT_SRC_REGEX = /<script[^>]*src=["']([^"']+)/g

使用正則表達式從HTML中提取所有<script>標簽的src屬性。注意正則表達式末尾的g標志表示全局匹配,因此需要手動重置lastIndex屬性。

4、比較
const hasChanges = cachedScriptSources.some((src, index) => src !== newScriptSources[index])

使用Array.some()方法比較兩個數(shù)組,只要發(fā)現(xiàn)任何一個位置的資源路徑不同,就判定為有更新。

拓展:uniapp更新檢測

// utils/updateManager.js

class UpdateManager {
  constructor() {
    this.updateManager = null
    this.hasUpdate = false
    this.init()
  }

  init() {
    // #ifdef MP-WEIXIN
    if (uni.getUpdateManager) {
      this.updateManager = uni.getUpdateManager()
      this.setupListeners()
    }
    // #endif
  }

  setupListeners() {
    if (!this.updateManager) return

    // 檢查更新結(jié)果回調(diào)
    this.updateManager.onCheckForUpdate((res) => {
      console.log('檢查更新結(jié)果:', res)
      if (res.hasUpdate) {
        this.hasUpdate = true
        // 可以在這里記錄有更新,但先不提示,等下載完成再提示
      }
    })

    // 更新下載完成回調(diào)
    this.updateManager.onUpdateReady(() => {
      this.showUpdateConfirm()
    })

    // 更新下載失敗回調(diào)
    this.updateManager.onUpdateFailed(() => {
      uni.showToast({
        title: '更新失敗,請檢查網(wǎng)絡',
        icon: 'none'
      })
    })
  }

  showUpdateConfirm() {
    uni.showModal({
      title: '更新提示',
      content: '新版本已經(jīng)準備好,是否重啟應用?',
      success: (res) => {
        if (res.confirm) {
          // 強制重啟小程序
          this.updateManager.applyUpdate()
        } else {
          // 用戶拒絕,可以記錄狀態(tài),稍后提醒
          this.scheduleReminder()
        }
      }
    })
  }

  scheduleReminder() {
    // 5分鐘后再次提醒
    setTimeout(() => {
      if (this.hasUpdate) {
        this.showUpdateConfirm()
      }
    }, 5 * 60 * 1000)
  }

  checkUpdate() {
    if (this.updateManager) {
      this.updateManager.onCheckForUpdate(() => {})
    }
  }
}

export default new UpdateManager()

在App.vue中初始化

<script>
import updateManager from '@/utils/updateManager.js'

export default {
  onLaunch() {
    // 小程序更新檢測
    updateManager.checkUpdate()
  },
  
  onShow() {
    // 每次進入前臺檢查
    updateManager.checkUpdate()
  }
}
</script>

鏈接匯總

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容