常见问题

caoguanjie2023年9月27日大约 3 分钟约 910 字

常见问题

多页签切换会丢失history.state的参数

之前app视图导航open in new window文章,提过关于vue页面如何通过params传参

上面介绍了一个history.state页面传参不需要明文的传参方式,下面大体回顾一下:

// 只需在 push 时 将参数传递到 state 字段中,vue-router 就会将参数存到 window.history.state 中。
// 路由的定义与普通方式无异
const routes: Array<RouteRecordRaw> = [
  {
    name: "APage",
    path: "/Apage",
    component: () => import("@/views/WebVue/components/APage.vue"),
    meta: { title: 'A页面' }
  },
];

// 传参
router.push({
  name:'APage',
  state: { id: 123 }
})

// 获取参数
let id = window.history.state.id;
// 或者
const { id, name } = window.history.state;

发现问题

有开发成员在使用上面的传参方式中发现了新的bug,就是通过history.state传参后,通过框架的多页签切换去到其他页面,再通过多页签切换回来之后,history.state参数消失了,举个例子:页面a ----> 页面b ---> 页面a,页面a通过tabviews组件跳转到页面b,再通过页签回到页面a之后,a页面的history.state参数消失了

原因

开发者是通过主动调用router.push({state: 参数})进行跳转页面传参的,然而多页签组件/src/layout/components/TagsView/index.vue是通过<router-link>进行页面的切换。关键代码:

<router-link :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath, cache: true } as any" class="tags-view__item">
</router-link>

由上面的关键代码我们可以推断以下几点内容:

  1. to属性是没有包括history的传参的,因此切换tab的时候,不可能会带上history.state参数
  2. to属性实际上是往历史堆栈新增一条历史记录的,而不是后退,当新增历史记录时,没有带上state参数的话,自然不会存在history.state这个api中

解决方案

  1. Router对象拓展一个state属性,存放每个路径需要history.state参数
/**
 * 重写router的push、go方法后,需要拓展cache属性
 */
declare module 'vue-router' {
    interface ObjectOf<state> {
        [fullPath: string]: state
    }
    interface Router {
        go(delta: number, option?: { cache: boolean }): void;
        back(option?: { cache: boolean }): void;
        forward(option?: { cache: boolean }): void;
        reload(to: RouteLocationRaw): Promise<NavigationFailure | void | undefined>
        /** 判断是否需要刷新组件 */
        routerRefresh: boolean
        /** 判断是否需要对当前组件进行缓存 */
        keepAlive: boolean
        /** 新增state对象,通过fullpath作为键,history.state作为值,进行保存参数 */
        state: ObjectOf<any>
    }
}
  1. 重写router.pushrouter.replace的方法,把每个路由的history.state参数保存到router.state对象里面,关键代码如下:
 /* =======================记录history.state的值  of  start============================================= */
if (!router.state) {
    // 初始化state属性
    router.state = {}
    // 这步是关键代码,当页面刷新时,当前路由的history.state对象虽然保留了,但是并没有记录在router.state属性中。因此这里要做一个预防操作
    // 通过解构赋值得到stateParams,back/current/forward这些参数是vue-router源码中要记录在history api的固定属性。
    const { back, current, forward, replaced, position, scroll, ...stateParams } = history.state;
    if (Object.keys(stateParams).length !== 0) {
        router.state[currentRoute.value.fullPath] = stateParams
    }
}
if (!location.state && router.state[_router.fullPath]) {
    location.state = router.state[_router.fullPath]
    to[0] = location
}

if (location.state) {
    // 如果前往的路径没值,需要赋值,如果前往的路由有值,也需要重新赋值,不然保存的参数就是旧的了。
    router.state[_router.fullPath] = location.state
}
/* =======================记录history.state的值  of  end============================================= */
  1. 前端们只需要把文件main/src/router/utils.ts最新的代码进行更新,复制到你们的项目,即可以解决问题,不需要调整其他页面
上次编辑于: 2023/9/27 08:55:57
贡献者: caoguanjie
Loading...