import { ref, toRefs } from 'vue' import { createRouter, RouterOptions, RouteRecordRaw, RouteLocationNormalized } from 'vue-router' interface historyRoute { name: string; fullPath: string; } interface HistoryState { historyRoutes: historyRoute[]; // 历史路由列表 excludeViews: string[]; // 不缓存的视图 actionName: '' | 'push' | 'replace' | 'forward' | 'back'; // 当前路由动作 transitionName: '' | 'route-out' | 'route-in'; // 前进后退动画 goStep: number; // 前进后退步数 } export default new (class { private storageKey = 'history_mobile' private state = ref({ historyRoutes: [], excludeViews: [], actionName: '', transitionName: '', goStep: 0 }) constructor() { const state = sessionStorage.getItem(this.storageKey) if (state) { // 合并赋值,防止 sessionStorage 获取的值有问题 this.state.value = { ...this.state.value, ...JSON.parse(state) } } } getState() { return this.state.value } getStateRef() { return toRefs(this.state.value) } /** * 创建路由 * @param options * @returns */ create = (options: RouterOptions) => { const router = createRouter(options) const { push, replace, go, forward, back } = router const { excludeViews, actionName, goStep } = this.getStateRef() // 添加 router.push = (to: RouteRecordRaw) => { actionName.value = 'push' return push(to) } // 替换 router.replace = (to: RouteRecordRaw) => { actionName.value = 'replace' return replace(to) } // 前进后退 router.go = (delta: number) => { goStep.value = delta if (delta > 0) { actionName.value = 'forward' } if (delta < 0) { actionName.value = 'back' } go(delta) } // 前进 router.forward = () => { actionName.value = 'forward' forward() } // 后退 router.back = () => { actionName.value = 'back' back() } router.beforeResolve((to) => { this.addHistory(to) }) router.afterEach(() => { excludeViews.value = [] }) return router } /** * 添加历史记录 * @param route */ private addHistory = (route: RouteLocationNormalized) => { const { historyRoutes, excludeViews, actionName, transitionName, goStep } = this.getStateRef() const newRoute = { name: route.name as string, fullPath: route.fullPath } // 如果是替换动作,必定是前进 if (actionName.value === 'replace') { const lastIndex = historyRoutes.value.length - 1 const lastView = historyRoutes.value[lastIndex] if (lastView) { excludeViews.value.push(lastView.name as string) historyRoutes.value[lastIndex] = newRoute // 更新最后一条记录 } else { historyRoutes.value.push(newRoute) } transitionName.value = 'route-in' // 前进动画 } else { // 倒序查找路由所在的位置 const index = (() => { for (let i = historyRoutes.value.length - 1; i >= 0; i--) { if (historyRoutes.value[i].fullPath === route.fullPath) { return i } } return -1 })() if (index > -1) { if (actionName.value === 'push') { historyRoutes.value.push(newRoute) transitionName.value = 'route-in' //前进动画 } else { if (historyRoutes.value.length > 1) { const i = index + 1 const n = historyRoutes.value.length - i excludeViews.value = historyRoutes.value.map((e) => e.name).slice(-n) // 返回数组最后位置开始的n个元素 historyRoutes.value.splice(i, n) // 从i位置开始删除后面所有元素(包括i) } transitionName.value = 'route-out' //后退动画 } } else { if (goStep.value < 0) { const start = historyRoutes.value.length + goStep.value if (start) { historyRoutes.value.splice(start) } } // 忽略重定向的页面 if (route.redirectedFrom) { transitionName.value = 'route-in' // 前进动画 } else { historyRoutes.value.push(newRoute) if (historyRoutes.value.length > 1) { transitionName.value = 'route-in' // 前进动画 } } } } actionName.value = '' goStep.value = 0 // 处理对象循环引用 // const getCircularReplacer = () => { // const seen = new WeakSet() // return (key: string, value: unknown) => { // if (typeof value === 'object' && value !== null) { // if (seen.has(value)) { // return // } // seen.add(value) // } // return value // } // } sessionStorage.setItem(this.storageKey, JSON.stringify(this.state.value)) } })