日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > HTML >内容正文

HTML

实践总结 3 种前端部署后页面检测版本的方法

發(fā)布時(shí)間:2024/1/11 HTML 69 coder
生活随笔 收集整理的這篇文章主要介紹了 实践总结 3 种前端部署后页面检测版本的方法 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

領(lǐng)導(dǎo):為什么每次項(xiàng)目部署后,有的用戶要清緩存才能看到最新的頁(yè)面

我:瀏覽器有默認(rèn)的緩存策略,如果服務(wù)器在響應(yīng)頭中沒(méi)有禁用緩存,那么瀏覽器每次請(qǐng)求頁(yè)面會(huì)先看看緩存里面有沒(méi)有,有的話從緩存取,造成還是取的舊頁(yè)面。正常來(lái)說(shuō),用戶只需要點(diǎn)擊刷新按鈕,刷新一下頁(yè)面就好了,不必清除瀏覽器緩存刷新。

領(lǐng)導(dǎo):為什么緩存這么嚴(yán)重,有的用戶清除緩存刷新還是不行,關(guān)掉瀏覽器重新進(jìn)來(lái)還是不行,要重啟電腦才有效。

我:要重啟電腦?這 。。。。。。用戶都這樣么,還是只有一小部分用戶。

領(lǐng)導(dǎo):不是所有的用戶,有個(gè)別用戶會(huì)出現(xiàn)這種情況

我:那可能得到用戶電腦上看看了

每次需求投產(chǎn)后,因?yàn)橛芯彺鎲?wèn)題導(dǎo)致用戶看到的還是舊版內(nèi)容,使用過(guò)程中出現(xiàn)了問(wèn)題,聯(lián)系我們才知道項(xiàng)目更新了,用戶體驗(yàn)不好;

于是查找資料,尋找合適的方案,根據(jù) 評(píng)論區(qū) 的討論,實(shí)踐總結(jié)了下面 3 種前端部署后頁(yè)面檢測(cè)版本更新的方法

當(dāng)檢測(cè)到版本更新則及時(shí)通知用戶,用戶可以選擇是否立即更新,并不會(huì)影響用戶當(dāng)前進(jìn)行的業(yè)務(wù);

下面以 vue 項(xiàng)目為例

1、輪詢打包后的 index.html,比較生成的 js 文件的 hash

項(xiàng)目打包后,index.html 會(huì)包含打包后的 js 文件,這些文件的文件名包含的 hash 將會(huì)和上一次打包的不同,比較 hash 也就能判斷是否有版本更新;

let firstV = [] //記錄初始獲得的 script 文件字符串
let currentv = [] //記錄當(dāng)前獲得的 script 文件字符串

// 獲得的文件字符串類似這樣 `<script src="/js/chunk-vendors.1234fff.js"></script>`

async function getHtml() {
let res = await axios.get('/index.html?date=' + Date.now())
    if (res.status == '200') {
        let text = res.data
        if (text) {
            // 解析 html 內(nèi)容,匹配 script 字符串
            let reg = /<script([^>]+)><\/script>/ig
            return text.match(reg) 
        }
    }
    return []
}
function isEqual(a, b) {
    return a.length = Array.from(new Set(a.concat(b))).length
}

export async function checkIfNewVersion() {

    firstV = await getHtml()

    window.checkVersionInterval && clearInterval(window.checkVersionInterval)

    window.checkVersionInterval = setInterval(async () =>{

        currentV = await getHtml()
        console.log(firstV,currentv)
        // 當(dāng)前 script hash 和初始的不同時(shí),說(shuō)明已經(jīng)更新
        if(!isEqual(firstV, currentv)) {
            console.log('已更新')
        }
    },3000)
}

// 文檔可見(jiàn)時(shí)檢測(cè)版本是否更新
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "visible") {
    checkIfNewVersion();
  } else {
    window.checkVersionInterval && clearInterval(window.checkVersionInterval)
  }
});

getHtml() 得到的結(jié)果示例如下:

[
    '<script src="/js/chunk-vendors.1234fff.js"></script>',
    '<script src="/js/app.1234fff.js"></script>',
]

改動(dòng)了一點(diǎn)業(yè)務(wù)代碼后,再次打包,上面 app.js 的 hash 就會(huì)發(fā)生變化

[
    '<script src="/js/chunk-vendors.1234fff.js"></script>',
    '<script src="/js/app.12ed5ca.js"></script>',
]

比較兩個(gè)的結(jié)果,如果結(jié)果不一樣,則代表有版本更新。

2、HEAD 方*詢響應(yīng)頭中的 etag

ETag 是資源的特定版本的標(biāo)識(shí)符。當(dāng)資源內(nèi)容發(fā)生變化時(shí),會(huì)生成新的 ETag
HEAD?方法請(qǐng)求資源的響應(yīng)頭信息,服務(wù)器不會(huì)返回響應(yīng)體,可以節(jié)省帶寬資源;

這里可以輪詢打包后的 index.html,取兩次響應(yīng)頭中的 eTag 比較,如果不同,說(shuō)明版本更新了;前提是服務(wù)器沒(méi)有禁用緩存。

let firstEtag = `` //記錄第一次進(jìn)來(lái)請(qǐng)求獲得的 etag
let currentEtag = `` //記錄當(dāng)前的 etag,會(huì)不斷的刷新

async function getEtag(){
    let res = await axios.head('/index.html')
    if(res.status == '200'){
        if(res.headers && res.headers.etag){
            return res.headers.etag
        }
    }
    return ''
}

export async function checkEtag() {

    firstEtag = await getEtag()

    window.checkEtagInterval && clearInterval(window.checkEtagInterval)

    window.checkEtagInterval = setInterval(async() =>{
        // 每隔一定時(shí)間請(qǐng)求最新的 etag
        currentEtag = await getEtag()
        // 當(dāng)前最新的 currentEtag 和初始 firstEtag 進(jìn)行比較,不同則說(shuō)明資源更新了;
        if(firstEtag && currentEtag && firstEtag!==currentEtag){
            console.log('已更新')
        }
    },3000)
}

// 文檔可見(jiàn)時(shí)檢測(cè)版本是否更新
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "visible") {
    checkEtag();
  } else {
    window.checkEtagInterval && clearInterval(window.checkEtagInterval)
  }
});

3、監(jiān)聽(tīng) git commit hash 變化

項(xiàng)目改動(dòng)提交 git 時(shí)會(huì)生成唯一的 hash 字符串,將最近提交的 commit hash 作為版本號(hào)保存在一個(gè) json 文件中;通過(guò)輪詢 json 文件,檢測(cè)里面的版本號(hào)是否和上次不同,不同則表示有版本更新;

監(jiān)聽(tīng) git commit hash 變化的好處是只要投產(chǎn)的版本有 git 提交記錄,而不管靜態(tài)文件變化還是代碼變化,都能檢測(cè)到版本更新;

在 vue.config.js 中引入 git-revision-webpack-plugin,該插件可獲取到項(xiàng)目本地 git 的最新提交 commit hash

const GitRevisionPlugin  = require('git-revision-webpack-plugin')
const gitRevision = new GitRevisionPlugin()

const { writeFile , existsSync } = require('fs')
if(existsSync('./public')){
    fs.writeFile(
        './public/version.json', 
        `{"commitHash":${JSON.stringify(gitRevision.commithash())}`, 
        (error) =>{}
    )
}

上面代碼使用 gitRevision.commithash() 獲取 commit hash,將其存入到 public/versionHash.json 文件中;

項(xiàng)目打包會(huì)執(zhí)行上面的代碼,生成后的 'versionHash.json' 文件類似這樣

// 示例
{ "commitHash" : "234fjsdr322f32f322f32f3g32g23jglk32gjkl32lg3" }

項(xiàng)目改動(dòng)后,提交改動(dòng)的地方后,再次打包,會(huì)將最新的 commit hash 存入到 public/versionHash.json

// 示例
{ "commitHash" : "234fjsdr322f3eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" }

然后在頁(yè)面中輪詢 '/versionHash.json',比較 commit hash ,檢測(cè)是否有更新

let firstCommitHash = ``
let currentCommitHash = ``

async function getCommitHash() {
    // 避免瀏覽器緩存加上時(shí)間戳參數(shù)
    let res = await axios.get('/versionHash.json?date=' + Date.now())
    if (res.status == '200') {
        if (res.data && res.data.commitHash) {
            return res.data.commitHash
        }
    }
    return ''
}

export async function checkCommitHash() {

    firstCommitHash = await getCommitHash()

    window.checkCommitHash && clearInterval(window.checkCommitHash)

    window.checkCommitHash = setInterval(async () => {
        // 輪詢 versionHash.json 文件
        currentCommitHash = await getCommitHash()

        if (firstCommitHash && currentCommitHash && firstCommitHash !== currentCommitHash) {

            console.log('已更新')
            // 作相應(yīng)處理
        }

    }, 3000)
}

關(guān)于檢測(cè)版本更新的時(shí)機(jī)

檢測(cè)時(shí)機(jī),我覺(jué)得有三種比較合適,可以靈活搭配上面的方法使用

  • 資源加載錯(cuò)誤時(shí)(常常發(fā)生在切換菜單時(shí)),檢測(cè)版本更新
  • 路由切換發(fā)生錯(cuò)誤時(shí)(也發(fā)生在切換菜單時(shí)或者當(dāng)前頁(yè)面引用其他路由時(shí)),檢測(cè)版本更新
  • 監(jiān)聽(tīng) visibilitychange + focus 事件
1、資源加載錯(cuò)誤時(shí)

前端部署后,某些資源已經(jīng)更新,當(dāng)切換菜單時(shí),可能會(huì)出現(xiàn)資源加載失敗的錯(cuò)誤(404)。此時(shí)可以使用 addEventListener('error') 捕獲資源加載錯(cuò)誤

window.addEventListener('error',(event) =>{
    // 檢測(cè)版本更新
    // window.location.reload()
},true)
2、路由切換發(fā)生錯(cuò)誤時(shí)

和上面的 addEventListener('error') 捕獲資源加載錯(cuò)誤類似, vue-routerrouter.onError() 方法可以捕獲到路由加載的錯(cuò)誤。

路由切換時(shí)某些資源加載失敗,會(huì)拋出 Loading chunk chunk-xxxx failed,可以用正則匹配它并作相應(yīng)處理;

router.onError((error) =>{
    let reg = /Loading.*?failed/g
    if(reg.test(error)){
        // 檢測(cè)版本更新
        // window.location.reload()
    }
})
3、監(jiān)聽(tīng) visibilitychange + focus 事件

visibilitychange:當(dāng)其選項(xiàng)卡的內(nèi)容變得可見(jiàn)或被隱藏時(shí),會(huì)在 document 上觸發(fā)?visibilitychange?事件。

當(dāng)用戶導(dǎo)航到新頁(yè)面、切換標(biāo)簽頁(yè)、關(guān)閉標(biāo)簽頁(yè)、最小化或關(guān)閉瀏覽器,或者在移動(dòng)設(shè)備上從瀏覽器切換到不同的應(yīng)用程序時(shí),該事件就會(huì)觸發(fā),其?visibilityState?為?hidden

在 pc 端,從瀏覽器切換到其他應(yīng)用程序并不會(huì)觸發(fā) visibilitychange 事件,所以加以 focus 輔佐;當(dāng)鼠標(biāo)點(diǎn)擊過(guò)當(dāng)前頁(yè)面(必須 focus 過(guò)),此時(shí)切換到其他應(yīng)用會(huì)觸發(fā)頁(yè)面的 blur 實(shí)踐;再次切回到瀏覽器則會(huì)觸發(fā) focus 事件;

document.addEventListener("visibilitychange", () => {
    if (document.visibilityState === "visible") {
        
        // 開(kāi)始檢測(cè)更新
    } else {
        
        // 結(jié)束檢測(cè)更新
    }
});

document.addEventListener('focus',() =>{

    // 開(kāi)始檢測(cè)更新
})

關(guān)于禁用緩存

禁用 html 緩存
<!-- HTTP/1.1 -->
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">

<!-- HTTP/1.0; 與 Cache-Control: no-cache 效果一致 -->
<meta http-equiv="Pragma" content="no-cache"> 

<!-- 如果在 Cache-Control 設(shè)置了 "max-age" 或者 "s-max-age" 指令,那么?`Expires`?頭會(huì)被忽略。-->
<meta http-equiv="Expires" content="0">

如果只在 html 中設(shè)置這個(gè)的話,只在 IE 中有效;若要在其他瀏覽器中生效,則需要對(duì)服務(wù)器設(shè)置禁用緩存;

nginx 設(shè)置禁用緩存
// 配置 html 和 htm 文件不緩存
location / {
    root   html;
    index  index.html index.htm;
    add_header Cache-Control "no-cache,no-store,must-revalidate";
}

總結(jié)

本文總結(jié)了 3 種前端部署后頁(yè)面檢測(cè)版本更新的方法;

  • 輪詢打包后的 index.html,比較生成的 js 文件的 hash
  • HEAD 方*詢響應(yīng)頭中的 etag
  • 監(jiān)聽(tīng) git commit hash 變化

3 種都有用武之地,看具體場(chǎng)景和需求;

監(jiān)聽(tīng) git commit hash 變化優(yōu)勢(shì)是可以檢測(cè)到靜態(tài)資源的變化;

HEAD 方*詢響應(yīng)頭中的 etag,優(yōu)勢(shì)是只需要取響應(yīng)頭中的字段,服務(wù)器不需要返回響應(yīng)體,節(jié)約資源;

總結(jié)

以上是生活随笔為你收集整理的实践总结 3 种前端部署后页面检测版本的方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。