整理思路
我在【115】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 中的内容。
实际测试中发现这种方法运行效率太低。每当改变前端路由的时候都要发请求。当然也可能是我的系统路由嵌套比较多的关系。不推荐大家使用第二种方法。
我一个写了两篇文章来说明这个问题:
【115】vue-router使用懒加载机制,在生产环境中,如何避免浏览器缓存Webpack 3 编译后生成的js路径,导致404错误。(一)
【116】vue-router使用懒加载机制,在生产环境中,如何避免浏览器缓存Webpack 3 编译后生成的js路径,导致404错误。(二)