vue-router 页面参数自动传递

有这么一种情况,各个不同的页面需要同样的参数,或者进入多层级页面后回跳之前页面时需要带回参数。每个页面手动传递参数是可以的,但是比较麻烦,并且容易丢失。

this.$router.push({
  path: 'xxx',
  qury: {
    ...this.$route.query
  }
})

如果某个页面忘记写传递参数的代码,bug 就产生了。所以,最好有一种全局统一的处理方法。比如在路由拦截里统一将来源页面(from)的部分参数传递到目标页面(to)。

通过 query 传递

url 里的参数和 query 里的属性是对应的,所以,通过 query 传递通用参数是首选。但是,vue-router 不允许直接修改 query,也不允许直接修改 fullPath(这是只读的)。所以通过 query 自动传递参数的实现比较曲折。

const constantRouterParams = ['aparam', 'bparam']
/**
 * 路由参数流转
 * @param {object} from
 * @param {object} to
 * @param {function} next
 */
routeParamsTransmit(from, to, next) {
  const sourceParams = from.query
  const targetParams = to.query
  // 如果目标路由必要参数不需要改变,直接放行,否则会陷入死循环。因为 next() 是放行,但 next(route) 是中断导航,开始新的跳转。
  if (
    constantRouterParams.every(key => {
    return (
      sourceParams[key] === undefined ||
      targetParams[key] !== undefined
    )
  })) {
    next()
    return
  }
  constantRouterParams.map(key => {
    // 只有源页面参数存在,且目标页面参数为 undefined 的时候才自动传参。这是为了避免目标页面设置的相同 key 的参数时会被覆盖掉。
    if (!isEmpty(sourceParams[key]) && (targetParams[key] === undefined)) {
      targetParams[key] = sourceParams[key]
    }
  })
  next({
    path: to.path,
    query: targetParams
  })
}

这样一来就可以将 from 的部分参数自动带给 to。但是,next(route) 的时候会出现重复导航的错误。这需要修复。

// 劫持 router 的 push 方法,因为路由的 beforeEach 里会对 next 方法改造,用于将 from 的部分参数自动带给 to。但是,这样会导致报错:NavigationDuplicated: Avoided redundant navigation to current location
const original = Router.prototype.push
Router.prototype.push = function push(location) {
  return original.call(this, location).catch(err => err)
}

通过 query 可以实现自动传参,但有潜在风险。比如原本是想 b 页面代替 a 页面,这样处理后会变成 b 页面添加到 a 的历史记录后面(猜测,未验证)。

通过 meta 传递

路由基本都有 meta 属性,一般我们会在里面配置一些页面信息。所以也可以在这里传递页面参数。目的页面通过 this.$route.meta.xxx 取值。写法上和通过 query 差不多,只是参数变成从 meta 到 meta,并且 meta 参数无法反映到 url 中。一旦刷新页面,参数就会丢失。但通过 meta 传递也可以避免路由报错及魔改操作。


全局参数除了路由传递,更常见的是通过 vuex 或者 storage 保存。但这两种方法也都有其缺点。vuex 刷新会丢失。localStorage 会存在数据污染的问题。sessionStorage……也许更适合?嗯……也许下次还是用 sessionStorage 吧,标签生命周期挺符合需求的。