import { ref, toRefs, readonly } from 'vue' import { createRouter, RouterOptions, RouteRecordRaw, RouteLocationNormalized } from 'vue-router' interface HistoryState { history: RouteLocationNormalized[]; // 已访问的路由列表 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({ history: [], excludeViews: [], actionName: '', transitionName: '', goStep: 0, }) /** 只读状态 */ state; constructor() { const state = sessionStorage.getItem(this.storageKey); if (state) { this._state.value = JSON.parse(state); } this.state = readonly(toRefs(this._state.value)); } /** * 创建路由 * @param options * @returns */ create = (options: RouterOptions) => { const router = createRouter(options); const { push, replace, go, forward, back } = router; const { actionName, goStep } = toRefs(this._state.value); // 添加 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(() => { const { excludeViews } = toRefs(this._state.value); excludeViews.value = []; }) return router; } /** * 添加历史记录 * @param route */ private addHistory = (route: RouteLocationNormalized) => { const { history, excludeViews, actionName, transitionName, goStep } = toRefs(this._state.value); // 如果是替换动作,必定是前进 if (actionName.value === 'replace') { const lastIndex = history.value.length - 1; const lastView = history.value[lastIndex]; if (lastView) { excludeViews.value.push(lastView.name as string); history.value[lastIndex] = route; // 更新最后一条记录 } else { history.value.push(route); } transitionName.value = 'route-in'; // 前进动画 } else { // 倒序查找路由所在的位置 const index = (() => { for (let i = history.value.length - 1; i >= 0; i--) { if (history.value[i].fullPath === route.fullPath) { return i; } } return -1; })(); if (index > -1) { if (actionName.value === 'push') { history.value.push(route); transitionName.value = 'route-in'; //前进动画 } else { if (history.value.length > 1) { const i = index + 1; const n = history.value.length - i; excludeViews.value = history.value.map((e) => e.name).slice(-n) as string[]; // 返回数组最后位置开始的n个元素 history.value.splice(i, n); // 从i位置开始删除后面所有元素(包括i) } transitionName.value = 'route-out'; //后退动画 } } else { if (goStep.value < 0) { const start = history.value.length + goStep.value if (start) { history.value.splice(start) } } // 忽略重定向的页面 if (route.redirectedFrom) { transitionName.value = 'route-in'; // 前进动画 } else { history.value.push(route); if (history.value.length > 1) { transitionName.value = 'route-in'; // 前进动画 } } } } actionName.value = ''; goStep.value = 0 sessionStorage.setItem(this.storageKey, JSON.stringify(this._state.value)); } })