1. 程式人生 > >【116】vue-router使用懶載入機制,在生產環境中,如何避免瀏覽器快取Webpack 3 編譯後生成的js路徑,導致404錯誤。(二)

【116】vue-router使用懶載入機制,在生產環境中,如何避免瀏覽器快取Webpack 3 編譯後生成的js路徑,導致404錯誤。(二)

整理思路

要解決這個問題,F5 重新整理是最好的解決辦法。但是每次釋出新版本後,都要求使用者主動按 F5 重新整理瀏覽器,會讓使用者覺得不方便。這對於快速迭代的產品來說尤其突出。

所以為了方便使用者使用,我們希望當前端頁面修改之後,系統能夠自動重新整理頁面。

現在來整理一下思路:

一、既然瀏覽器快取舊 js 檔名導致丟擲 404 錯誤,那麼就要捕獲這個異常。

二、捕獲異常之後,要監測一下客戶端的版本是否低於伺服器(如 Nginx )中的版本。如果是客戶端版本低造成的,自動 F5 重新整理;如果是其他原因,在控制檯打印出錯誤資訊。

三、注意在自動重新整理的時候,不能使用 window.location.reload() 函式重新整理。因為當瀏覽器丟擲載入頁面錯誤的時候,位址列還沒有更新地址。如果你利用給 window.location.href 賦值的方式重新整理頁面,那麼這個地址會被前端路由攔截到。所以有效的辦法是讓瀏覽器先跳轉到另外一個域名,再跳轉回來。

原始碼

我的所有部落格的原始碼都放在碼雲上,網址是https://gitee.com/zhangchao19890805/csdnBlog 。這篇部落格的程式碼在git專案中的 blog116資料夾裡面。公分為兩個專案 blog116redirect 是負責跳轉的;blog116main 是主要業務專案。

解決方案

第一步,要捕獲出錯的異常。這是在 vue-router 懶載入的時候出現的。vue-router 實際上是使用了 Webpack 程式碼分割的 api。為了方便的捕獲異常,我使用了 Webpack 3 新加的 import(“./your/path”) 函式來載入 .vue 檔案。Webpack 3 的 import 函式會返回 Promise,因此我在 import 函式後面加上 .catch(...)

函式就可以捕獲異常。vue-router 配置檔案內容如下:

import index from './index.vue';
import routerUtils from "./routerUtils.js";

export default [
    {
        path:'/',
        name: "index",
        component:index,
        children: [
            {
                path:"home",
                name: "home",
                component
: ()=>import("./home.vue").catch(routerUtils.catchImport) }, { path: "page1", name: "page1", component: ()=>import("./page1.vue").catch(routerUtils.catchImport) } ] } ]

第二步,捕獲異常之後,要監測一下客戶端的版本是否低於伺服器(如 Nginx )中的版本。如果是客戶端版本低造成的,自動 F5 重新整理;如果是其他原因,在控制檯打印出錯誤資訊。

因為是在瀏覽器地址改變之前出錯,所以每次前端路由跳轉的時候需要Vuex記錄下URL,程式碼如下:

router.beforeEach((to, from, next) => {
    // 記錄要跳轉過去的路徑,如果載入頁面失敗,就重新整理瀏覽器。
    store.dispatch("routerToPathAction", to.fullPath);
    next();
});

統一處理異常的程式碼如下:

import store from "./vuex/store.js"
import axios from "axios";
import localVersion from "./routerVersion.js";

const SITE_URL = "http://localhost";
const REDIRECT_SITE = "http://localhost:8081";

export default {
    async catchImport(err){
        try {
            // 發請求檢查版本
            let res = await axios.get(SITE_URL + "/version.json", {
                params:{r: Math.random()}
            });
            if (localVersion < res.data.data.version) {
                window.location.href = REDIRECT_SITE + "/r?p=" + encodeURI(store.getters.routerToPath);
            } else {
                console.error("err", err);
            }
        } catch (err2) {
            console.error("err2", err2);
        }
    }
}

第三步、注意在自動重新整理的時候,不能使用 window.location.reload() 函式重新整理。因為當瀏覽器丟擲載入頁面錯誤的時候,位址列還沒有更新地址。如果你利用給 window.location.href 賦值的方式重新整理頁面,那麼這個地址會被前端路由攔截到。所以有效的辦法是讓瀏覽器先跳轉到另外一個域名,再跳轉回來。

需要新建一個前端vue專案,這個專案對應我的碼雲中的 blog116/blog116redirect 。下面展示關鍵程式碼 。

router.js

import index from './index.vue';
import redirectVue from "./redirectVue.vue";
export default [
    {
        path:'/',
        name: "index",
        component:index,
        children: [
            // redirect
            {
                path: 'r',
                name: 'redirect',
                component: redirectVue
            }
        ]
    }
]

redirectVue.vue

<template>
    <div>正在跳轉...</div>
</template>
<script>
    export default {
        data(){
            return{};
        },

        mounted(){
            let redirectUrl = this.$route.query.p;
            let url = decodeURI(redirectUrl);
            window.location.href = "http://localhost" + url;
        }
    }
</script>
<style lang="scss" rel="stylesheet/scss" scoped></style>

總結第一種方法

這種解決方案有優點和缺點。

優點是,相對第二種解決方法(本文後面有討論),可以大大減少瀏覽器發起的請求數量,緩解伺服器壓力。

缺點也很明顯,瀏覽器必須等到丟擲異常的時候才會更新前端頁面,有可能更新不及時。而且如果瀏覽器快取了更新前的頁面,當瀏覽器再次進入同一個頁面的時候,有可能不會丟擲異常,而是繼續執行快取了的舊程式碼。

第二種方法:

簡單的說,就是在路由改變的時候,加一個總的路由守衛,只要路由有變化,就傳送請求,檢查本地版本和伺服器版本是否一致。如果一致,放行。如果不一致,頁面重新整理。重新整理方法和第一種一樣,跳轉到新的域名之後,再跳轉回來。跳轉程式碼同樣是 blog116/blog116redirect 中的內容。

實際測試中發現這種方法執行效率太低。每當改變前端路由的時候都要發請求。當然也可能是我的系統路由巢狀比較多的關係。不推薦大家使用第二種方法。

我一個寫了兩篇文章來說明這個問題: