背景
业务需求,实现跳转进页面的时候重新加载页面,后退的时候保持缓存。搜索了很多的回答,大概有几种方法:
1、keepalive判断router-view --> 缓存不生效。
2、includes --> 无法实现前进刷新
3、页面里做路由监听 --> 没有测试,太过于繁琐
方案
1、重写router的push和replace方法,记录路由的历史
2、跳转的方法里加入t参数,清除路由缓存,实现刷新
3、跳转回已有路由时,清除后面的记录,不会无限返回; exp a -> b -> c -> d, d跳转到b时,再返回时回到a
// App.vue
<keep-alive>
<router-view ref="routerView" :key="$route.fullPath"></router-view>
</keep-alive>
// router/index.js
Vue.use(VueRouter);
let historyList = [];
let checkFlag = false; // 判断路由记录返回标识
let previousPath = ''; // 前一个路由 用于返回后重定向
let targetPath = ''; //
router.afterEach(to => {
if (!historyList.includes(to.path) && !to.query.replace) historyList.push(to.path);
if (to.fullPath === previousPath) {
router.push(targetPath);
previousPath = '';
targetPath = '';
}
console.log('route each histroy--', JSON.stringify(historyList));
});
// 监听返回事件
window.addEventListener('popstate', () => {
console.log('popstate begin', checkFlag);
!checkFlag && historyList.pop();
checkFlag = false;
console.log('popstate end', JSON.stringify(historyList));
});
function checkRoute(location) {
const index = historyList.findIndex(path => path === location.path);
if (index !== -1) {
console.log('index--', index);
const backLen = index - historyList.length;
previousPath = historyList[index - 1];
historyList = historyList.slice(0, index);
checkFlag = true;
router.go(backLen);
console.log('1234 route back ---- backlength==', backLen);
return false;
}
return true;
}
const originalPush = VueRouter.prototype.push;
const originalReplace = VueRouter.prototype.replace;
// 重写原型上的push和replace方法,统一处理跳转和错误捕获
VueRouter.prototype.push = function (target) {
let location = target;
if (typeof target === 'string') {
const path = target.split('?')[0];
const query = parseQuery(target.split('?')?.[1] ?? '');
location = { path, query };
}
const result = checkRoute(location);
if (result) {
location.query = Object.assign({}, location.query, {
t: new Date().getTime()
});
console.log('push begain');
return originalPush.call(this, location).catch(err => console.log(err));
}
};
VueRouter.prototype.replace = function (target) {
let location = target;
if (typeof target === 'string') {
const path = target.split('?')[0];
const query = parseQuery(target.split('?')?.[1] ?? '');
location = { path, query };
}
const result = checkRoute(location);
if (result) {
location.query = Object.assign({}, location.query, {
t: new Date().getTime(),
replace: true
});
console.log('push begain');
return originalReplace.call(this, location).catch(err => console.log(err));
}
};
2023/2/21更新
v3版本有所不同,代码如下
// APP.vue
<template lang="pug">
div
router-view(v-slot='{ Component }')
keep-alive
component(:is='Component', :key='route.fullPath')
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';
export default defineComponent({
setup() {
const route = useRoute();
return {
route
};
}
});
</script>
// 路由router.ts
import { Router, RouteLocationNormalized, parseQuery } from 'vue-router';
export default function intercept(router: Router) {
let historyList: Array<string> = [];
let routeReplace = false; // replace方法不添加历史
let checkFlag = false; // 判断路由记录返回标识
router.afterEach((to: RouteLocationNormalized) => {
window.scrollTo(0, 0);
document.title = String(to.meta?.title);
if (!historyList.includes(to.path) && routeReplace === false) historyList.push(to.path);
console.log('after each routerHistory===', JSON.parse(JSON.stringify(historyList)));
});
window.addEventListener('popstate', () => {
console.log('window popstate begain', window.history, historyList.length);
!checkFlag && historyList.pop();
checkFlag = false;
console.log('widow popstate routerHistory===', JSON.parse(JSON.stringify(historyList)));
});
function checkRoute(location: any) {
const index = historyList.findIndex(path => path === location.path);
console.log('index===', index);
if (index !== -1) {
const backLen = index + 1 - historyList.length;
historyList = historyList.slice(0, index);
console.log('histroy lsit', JSON.parse(JSON.stringify(historyList)));
checkFlag = true;
router.go(backLen);
}
}
const originalPush = router.push;
const originalReplace = router.replace;
// 重写原型上的push和replace方法,统一处理跳转和错误捕获
router.push = function push(target: any) {
let location: any = target;
if (typeof target === 'string') {
console.log(target.split('?'));
const path = target.split('?')[0];
const query = parseQuery(target.split('?')?.[1] ?? '');
location = { path, query };
}
console.log('push location----', location);
routeReplace = false;
checkRoute(location);
location.query = Object.assign({}, location.query, {
t: new Date().getTime()
});
return originalPush.call(this, location).catch(err => console.log(err));
};
router.replace = function replace(target: any) {
let location: any = target;
if (typeof target === 'string') {
const path = target.split('?')[0];
const query = parseQuery(target.split('?')?.[1] ?? '');
location = { path, query };
}
console.log('replace location----', location);
routeReplace = true;
checkRoute(location);
location.query = Object.assign({}, location.query, {
t: new Date().getTime()
});
return originalReplace.call(this, location).catch(err => console.log(err));
};
return router;
}
2023/7/28更新
上述v3版本的代码在某些情况下会出现错误,而且定时器跳转在体验上不太友好,因此我在这段代码的基础上做了一些修改,目前测试下来没有问题,完整的代码如下
export default function intercept(router: Router) {
let historyList: Array<string> = [];
let checkFlag = false; // 判断路由记录返回标识
router.beforeEach(
(
to: RouteLocationNormalized,
from: RouteLocationNormalized,
next: NavigationGuardNext
) => {
if (checkFlag) {
checkFlag = false;
next({
...to,
replace: true,
query: { ...to.query, t: new Date().getTime() },
});
} else {
next();
}
}
);
router.afterEach(
(to: RouteLocationNormalized, from: RouteLocationNormalized) => {
window.scrollTo(0, 0);
document.title = String(to.meta?.title);
traceViewManual();
if (!historyList.includes(to.path) && !to.query.replace)
historyList.push(to.path);
if (
from.path === historyList[historyList.length - 1] &&
to.path === historyList[historyList.length - 2]
) {
historyList.pop();
}
console.log(
'after each routerHistory===',
JSON.parse(JSON.stringify(historyList))
);
}
);
window.addEventListener('popstate', () => { });
function checkRoute(location: any) {
const index = historyList.findIndex((path) => path === location.path);
// tab页不处理
if (index !== -1) {
const backLen = index - (historyList.length - 1);
historyList = historyList.slice(0, index + 1);
checkFlag = true;
router.go(backLen);
return false;
}
return true;
}
const originalPush = router.push;
const originalReplace = router.replace;
// 重写原型上的push和replace方法,统一处理跳转和错误捕获
router.push = function push(target: any) {
let location: any = target;
if (typeof target === 'string') {
const path = target.split('?')[0];
const query = parseQuery(target.split('?')?.[1] ?? '');
location = { path, query };
}
const result = checkRoute(location);
if (result) {
location.query = { ...location.query, t: new Date().getTime() };
return originalPush
.call(this, location)
.catch((err: any) => console.log(err));
}
return Promise.resolve(undefined);
};
router.replace = function replace(target: any) {
let location: any = target;
if (typeof target === 'string') {
const path = target.split('?')[0];
const query = parseQuery(target.split('?')?.[1] ?? '');
location = { path, query };
}
const result = checkRoute(location);
if (result) {
location.query = {
...location.query,
t: new Date().getTime(),
replace: true,
};
console.log('replace begain');
return originalReplace
.call(this, location)
.catch((err: any) => console.log(err));
}
return Promise.resolve(undefined);
};
return router;
}
TIP
后续如果有问题继续更新。。。