animateRouter.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { ref, toRefs } from 'vue'
  2. import { createRouter, RouterOptions, RouteRecordRaw, RouteLocationNormalized } from 'vue-router'
  3. interface historyRoute {
  4. name: string;
  5. fullPath: string;
  6. }
  7. interface HistoryState {
  8. historyRoutes: historyRoute[]; // 历史路由列表
  9. excludeViews: string[]; // 不缓存的视图
  10. actionName: '' | 'push' | 'replace' | 'forward' | 'back'; // 当前路由动作
  11. transitionName: '' | 'route-out' | 'route-in'; // 前进后退动画
  12. goStep: number; // 前进后退步数
  13. }
  14. export default new (class {
  15. private storageKey = 'history_mobile'
  16. private state = ref<HistoryState>({
  17. historyRoutes: [],
  18. excludeViews: [],
  19. actionName: '',
  20. transitionName: '',
  21. goStep: 0
  22. })
  23. constructor() {
  24. const state = sessionStorage.getItem(this.storageKey)
  25. if (state) {
  26. // 合并赋值,防止 sessionStorage 获取的值有问题
  27. this.state.value = {
  28. ...this.state.value,
  29. ...JSON.parse(state)
  30. }
  31. }
  32. }
  33. getState() {
  34. return this.state.value
  35. }
  36. getStateRef() {
  37. return toRefs(this.state.value)
  38. }
  39. /**
  40. * 创建路由
  41. * @param options
  42. * @returns
  43. */
  44. create = (options: RouterOptions) => {
  45. const router = createRouter(options)
  46. const { push, replace, go, forward, back } = router
  47. const { excludeViews, actionName, goStep } = this.getStateRef()
  48. // 添加
  49. router.push = (to: RouteRecordRaw) => {
  50. actionName.value = 'push'
  51. return push(to)
  52. }
  53. // 替换
  54. router.replace = (to: RouteRecordRaw) => {
  55. actionName.value = 'replace'
  56. return replace(to)
  57. }
  58. // 前进后退
  59. router.go = (delta: number) => {
  60. goStep.value = delta
  61. if (delta > 0) {
  62. actionName.value = 'forward'
  63. }
  64. if (delta < 0) {
  65. actionName.value = 'back'
  66. }
  67. go(delta)
  68. }
  69. // 前进
  70. router.forward = () => {
  71. actionName.value = 'forward'
  72. forward()
  73. }
  74. // 后退
  75. router.back = () => {
  76. actionName.value = 'back'
  77. back()
  78. }
  79. router.beforeResolve((to) => {
  80. this.addHistory(to)
  81. })
  82. router.afterEach(() => {
  83. excludeViews.value = []
  84. })
  85. return router
  86. }
  87. /**
  88. * 添加历史记录
  89. * @param route
  90. */
  91. private addHistory = (route: RouteLocationNormalized) => {
  92. const { historyRoutes, excludeViews, actionName, transitionName, goStep } = this.getStateRef()
  93. const newRoute = {
  94. name: route.name as string,
  95. fullPath: route.fullPath
  96. }
  97. // 如果是替换动作,必定是前进
  98. if (actionName.value === 'replace') {
  99. const lastIndex = historyRoutes.value.length - 1
  100. const lastView = historyRoutes.value[lastIndex]
  101. if (lastView) {
  102. excludeViews.value.push(lastView.name as string)
  103. historyRoutes.value[lastIndex] = newRoute // 更新最后一条记录
  104. } else {
  105. historyRoutes.value.push(newRoute)
  106. }
  107. transitionName.value = 'route-in' // 前进动画
  108. } else {
  109. // 倒序查找路由所在的位置
  110. const index = (() => {
  111. for (let i = historyRoutes.value.length - 1; i >= 0; i--) {
  112. if (historyRoutes.value[i].fullPath === route.fullPath) {
  113. return i
  114. }
  115. }
  116. return -1
  117. })()
  118. if (index > -1) {
  119. if (actionName.value === 'push') {
  120. historyRoutes.value.push(newRoute)
  121. transitionName.value = 'route-in' //前进动画
  122. } else {
  123. if (historyRoutes.value.length > 1) {
  124. const i = index + 1
  125. const n = historyRoutes.value.length - i
  126. excludeViews.value = historyRoutes.value.map((e) => e.name).slice(-n) // 返回数组最后位置开始的n个元素
  127. historyRoutes.value.splice(i, n) // 从i位置开始删除后面所有元素(包括i)
  128. }
  129. transitionName.value = 'route-out' //后退动画
  130. }
  131. } else {
  132. if (goStep.value < 0) {
  133. const start = historyRoutes.value.length + goStep.value
  134. if (start) {
  135. historyRoutes.value.splice(start)
  136. }
  137. }
  138. // 忽略重定向的页面
  139. if (route.redirectedFrom) {
  140. transitionName.value = 'route-in' // 前进动画
  141. } else {
  142. historyRoutes.value.push(newRoute)
  143. if (historyRoutes.value.length > 1) {
  144. transitionName.value = 'route-in' // 前进动画
  145. }
  146. }
  147. }
  148. }
  149. actionName.value = ''
  150. goStep.value = 0
  151. // 处理对象循环引用
  152. // const getCircularReplacer = () => {
  153. // const seen = new WeakSet()
  154. // return (key: string, value: unknown) => {
  155. // if (typeof value === 'object' && value !== null) {
  156. // if (seen.has(value)) {
  157. // return
  158. // }
  159. // seen.add(value)
  160. // }
  161. // return value
  162. // }
  163. // }
  164. sessionStorage.setItem(this.storageKey, JSON.stringify(this.state.value))
  165. }
  166. })