复制标签页导致的Vue动态路由失效问题解决思路
先透一下题,本次问题是因为未正确理解Vue和浏览器的整体生命周期导致的问题。
背景
自己开发的快速开发框架,前端自己开发的动态路由,但我发现通过浏览器复制标签页打开的新标签页无法加载动态路由。
打开的界面完全空白,因为找不到相关路由。
开发环境
这里主要说明一下相关的主要环境:
Vue 2.7、Vue Router 3.6
排查问题
现在的动态路由逻辑
下面是动态路由的逻辑是:
1、监听浏览器的刷新事件,该事件会记录需要刷新动态路由的标识位。代码如下所示:
window.onbeforeunload = function () { // 刷新会触发这个事件if(window.localStorage.getItem("resources")){sessionStorage.setItem("RefreshRouter", false);}
};
同时在App.vue文件中的created生命周期方法中也进行设置该值:
export default {name: 'App',created(){if(window.localStorage.getItem("resources")){sessionStorage.setItem("RefreshRouter", false);}}
}
2、设置Vue Router的全局前置守卫配置,每次跳转路由时检查RefreshRouter标识位。
import router from '@/core/router.js'
// 设置全局前置守卫配置
router.beforeEach((to, from, next) => {RouterUtils.refreshRouter(router,to).then(() => {/* 其他逻辑 */next()})
});
这里的关键方法是RouterUtils中的refreshRouter方法,下面是这个方法的完整代码:
refreshRouter: function (router,to) {return new Promise((resolve, reject) => {try {if (window.localStorage.getItem("resources") && !JSON.parse(sessionStorage.getItem("RefreshRouter"))) {sessionStorage.setItem("RefreshRouter", true);this.addRoutes(JSON.parse(window.localStorage.getItem("resources"))).then(() => {router.push(to);resolve(); // 所有路由添加完成后调用 resolve()});} else {resolve();}} catch (error) {reject(error);}});
},
这个方法中主要检查了当前缓存中是否存在动态路由数据,如果存在再检查当前刷新动态路由的标识位,如果都符合要求,则执行动态添加路由操作并跳转到目标路由地址。
寻找问题
在目前的动态路由逻辑中,即使在浏览器地址栏复制路由地址 -> 粘贴路由地址
直接访问也是可以正常加载动态路由并跳转的,如下图所示:
为了直观的看到加载顺序,分别在window.onbeforeunload
中、refreshRouter
方法中各添加数字顺序打印。
代码示例如下所示:
window.onbeforeunload = function () { // 刷新会触发这个事件console.log(1)if(window.localStorage.getItem("resources")){sessionStorage.setItem("RefreshRouter", false);}
};export default {name: 'App',created(){if(window.localStorage.getItem("resources")){console.log(1)sessionStorage.setItem("RefreshRouter", false);}}
}refreshRouter: function (router,to) {return new Promise((resolve, reject) => {try {if (window.localStorage.getItem("resources") && !JSON.parse(sessionStorage.getItem("RefreshRouter"))) {console.log(2)sessionStorage.setItem("RefreshRouter", true);this.addRoutes(JSON.parse(window.localStorage.getItem("resources"))).then(() => {router.push(to);resolve(); // 所有路由添加完成后调用 resolve()});} else {console.log(3)resolve();}} catch (error) {reject(error);}});
},
先来一个正常的执行过程,在地址栏直接粘贴路由地址查看控制台打印顺序,正常加载动态路由:
通过复制标签页打开一个新窗口,查看控制台日志打印顺序:
理想中的状态应该是先设置刷新路由标识未,再进行加载动态路由,但通过复制标签页打开新窗口顺序是相反的,有了解其中原因的朋友可以说一下,我这里就不继续往下深究了。
解决问题
将原来监听刷新事件的方法和App.vue中created生命周期方法中设置刷新动态路由标识的代码全部删除。
在main.js中初始化Vue Router之前增加动态路由标识,此步骤能保证不管什么生命周期都会被触发:
import router from '@/core/router.js'
if(window.localStorage.getItem("resources")){console.log(1)sessionStorage.setItem("RefreshRouter", false);
}
// 设置全局前置守卫配置
router.beforeEach((to, from, next) => {RouterUtils.refreshRouter(router,to).then(() => {// 其他逻辑next()})
});
验证一下结果是否正确,通过刷新界面、复制粘贴地址栏地址、复制标签页结果都可以正常展示界面内容,动态路由都可以被正常加载,控制台查看一下日志打印是否正常:
OK,大功告成!
总结
本文主要是因为没有理解透彻浏览器中Vue项目的生命周期,导致自己设计的动态路由组件在复制标签页打开新页面时无法正常加载动态路由,导致页面白屏。
经过一系列调试后,在main.js中初始化Vue Router前就设置标识位,保证在各种情况下都可以正常加载动态路由。