前端項目發版後用戶需要手動刷新頁面獲取的解決方案

背景

我們的項目是後臺管理系統,框架是Vue,構建工具是Webpack

問題

每一次修改代碼後打包發佈到生產環境後,用戶需要手動刷新才能獲取到最新的代碼。

原因

Webpack打包之後會根據文件內容生成一個hash值,然後再按照[name].[hash].js的格式生成文件名;

然後再根據文件的路由對應關係生成一個runtime.[hash].js的文件,這個文件會監控瀏覽器的路由變化去服務器加載對應的js文件。

runtime.[hash].js文件是放在index.html中,在開始進入系統時已經加載。

所以當服務器重新發版之後,生成的文件會出現hash值改變,這時候當用戶跳轉路由時,請求加載的文件名在服務器上已經找不到了,就會報錯:Loading chunk [name].[hash].js failed

方案

思路:既然能監控到加載js的錯誤信息,那麼就可以在處理錯誤信息的代碼中加入刷新頁面的功能,讓頁面自動去刷新頁面,然後再獲取最新的runtime.[hash].js文件,用戶再次點擊時就可以正常訪問了。

按照思路,分爲以下2步:

  1. 找到處理錯誤信息的代碼,加入刷新頁面邏輯

    經過幾番查找,處理錯誤信息的代碼在runtime.[hash].js中,如下:

    var u = new Error;
        d = function(a) {
            o.onerror = o.onload = null,
            clearTimeout(b);
            var c = r[e];
            if (0 !== c) {
                if (c) {
                  var t = a && ("load" === a.type ? "missing" : a.type), 
                      f = a && a.target && a.target.src;
                  u.message = "Loading chunk " + e + " failed.\n(" + t + ": " + f + ")",
                  u.type = t,
                  u.request = f,
                  c[1](u)
                }
                r[e] = void 0
            }
        }
    

    難點在於,runtime.[hash].js是由webpack單獨生成的,我們不能通過項目中的代碼來修改,而且暫時沒有找到通過配置的方式來修改。

    目前的臨時解決方案:

    通過Loading chunk關鍵字在webpack模塊中找到了對應的文件,文件地址:webpack/lib/web/JsonpMainTemplatePlugin.js,找到處理錯誤代碼,並添加刷新頁面代碼:

    Template.indent([
        "if(chunk) {",
        Template.indent([
            "var errorType = event && (event.type === 'load' ? 'missing' : event.type);",
            "var realSrc = event && event.target && event.target.src;",
            "error.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';",
            "error.type = errorType;",
            "error.request = realSrc;",
            "chunk[1](error);"
        ]),
        "}",
        "location.reload(ture)", // 在此處添加刷新頁面代碼
        "installedChunks[chunkId] = undefined;"
    ]),
    

    重新執行npm run build命令就可以了

    問題的來了,自動佈署時會重新拉取webpack包,還是不能生效。

    解決方案就是自己Fork webpack項目,修改以上代碼後,再自己發佈一個npm包,

    然後將項目中的webpack引用名改成自己的npm包名,這樣就可以了,缺點就是後續的升級比較麻煩。

  2. 刷新頁面時,取消緩存,獲取最新的文件

    自動刷新解決了,發現瀏覽器並沒從服務器上拉取文件,原因就是瀏覽器緩存了。

    解決方案:在文件請求路徑添加參數,參數值設置爲hash值,比如:

    <script src="/static/js/runtime.68e22691.js?t=68e22691">
    <link href="/static/css/vendors.bbf38da36d1cd1d96a5e.css?t=cd1d96a5e"  rel="stylesheet">
    

    怎麼配置呢?

    找到webpackoutput,設置成如下:

    output: {
        path: OUTPUT_PATH,
        publicPath: '/',
        filename: 'static/js/[name].[chunkhash:8].js?t=[chunkhash:8]',
        chunkFilename: 'static/js/[name].[chunkhash:8].js?t=[chunkhash:8]'
      }
    

優化

按照目前的解決方案,確實可以實現不再需要手動刷新獲取最新的資源,但是有個問題就是刷新之後並不會跳轉到用戶點擊的頁面,還是停留在當前頁面,需要再點擊一次纔到跳轉。

所以我們可以在刷新之前,彈出一個確認框,告知用戶服務器發版或升級了,需要刷新頁面才能獲取最新的資源。用戶點擊確認之後再進行刷新操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章