背景
業(yè)務(wù)需求,實現(xiàn)跳轉(zhuǎn)進頁面的時候重新加載頁面愤钾,后退的時候保持緩存瘟滨。搜索了很多的回答,大概有幾種方法:
1能颁、keepalive判斷router-view --> 緩存不生效杂瘸。
2、includes --> 無法實現(xiàn)前進刷新
3伙菊、頁面里做路由監(jiān)聽 --> 沒有測試败玉,太過于繁瑣
方案
1敌土、重寫router的push和replace方法,記錄路由的歷史
2运翼、跳轉(zhuǎn)的方法里加入t參數(shù)返干,清除路由緩存,實現(xiàn)刷新
3血淌、跳轉(zhuǎn)回已有路由時矩欠,清除后面的記錄,不會無限返回悠夯; exp a -> b -> c -> d, d跳轉(zhuǎn)到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));
});
// 監(jiān)聽返回事件
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方法,統(tǒng)一處理跳轉(zhuǎn)和錯誤捕獲
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方法该默,統(tǒng)一處理跳轉(zhuǎn)和錯誤捕獲
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版本的代碼在某些情況下會出現(xiàn)錯誤,而且定時器跳轉(zhuǎn)在體驗上不太友好策彤,因此我在這段代碼的基礎(chǔ)上做了一些修改栓袖,目前測試下來沒有問題,完整的代碼如下
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方法店诗,統(tǒng)一處理跳轉(zhuǎn)和錯誤捕獲
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
后續(xù)如果有問題繼續(xù)更新裹刮。。庞瘸。