Vue.js 源码分析:路由参数传递与 query 处理

Vue.js 源码分析:路由参数传递与 query 处理

【免费下载链接】vue-analysis :thumbsup: Vue.js 源码分析 【免费下载链接】vue-analysis 项目地址: https://gitcode.com/gh_mirrors/vu/vue-analysis

在 Vue.js 应用开发中,路由参数传递是连接不同页面数据的核心机制。无论是动态路由参数(Params)还是查询字符串(Query),Vue-Router 都提供了灵活的处理方案。本文将从源码角度解析两种参数传递方式的实现原理,帮助开发者深入理解参数解析流程与最佳实践。

路由参数传递的两种方式

Vue-Router 支持两种主要参数传递方式,分别适用于不同场景:

1. 动态路由参数(Params)

使用场景:页面路径中表示资源唯一标识,如用户ID、商品编号等
实现方式:在路由配置中使用:参数名定义,通过$route.params访问

// 路由配置 [src/index.js](https://link.gitcode.com/i/ce6502334bdf2362b678c0d907da1a18)
const routes = [
  { path: '/user/:id', component: User }
]

// 跳转方式
<router-link to="/user/123">用户123</router-link>
// 或编程式导航
this.$router.push('/user/123')

// 组件中访问
this.$route.params.id // "123"

2. 查询字符串(Query)

使用场景:页面筛选条件、分页参数等非核心标识信息
实现方式:URL中?后的键值对,通过$route.query访问

// 跳转方式
<router-link to="/list?page=1&sort=desc">列表页</router-link>
// 或编程式导航
this.$router.push({ path: '/list', query: { page: 1, sort: 'desc' } })

// 组件中访问
this.$route.query.page // "1"
this.$route.query.sort // "desc"

参数解析的源码实现

1. 动态路由参数的匹配机制

路由匹配的核心逻辑在create-route-map.js中实现,该模块负责将路由配置转换为可匹配的路径规则:

// [src/create-route-map.js](https://link.gitcode.com/i/6416f0ee775b76edc1bb084b0c780fb7)
function addRouteRecord (
  pathList: Array<string>,
  pathMap: Dictionary<RouteRecord>,
  nameMap: Dictionary<RouteRecord>,
  route: RouteConfig,
  parent?: RouteRecord,
  matchAs?: string
) {
  const { path, name } = route
  // 处理路径中的动态参数
  const normalizedPath = normalizePath(path, parent, pathList)
  const record: RouteRecord = {
    path: normalizedPath,
    regex: pathToRegexp(normalizedPath, keys, options),
    keys, // 存储参数名信息
    components: route.components || { default: route.component },
    // ...其他属性
  }
  // 添加到路径映射表
  pathMap[normalizedPath] = record
}

路径匹配使用path-to-regexp库将路径字符串转换为正则表达式,例如/user/:id会被转换为匹配/user/123的正则,并提取id参数。

2. 参数提取与路由匹配

当调用router.push或点击<router-link>时,路由系统会执行transitionTo方法进行路径切换:

// [src/history/base.js](https://link.gitcode.com/i/32e8e625c2c0ea7ab0f4cd6f0192b032)
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  const route = this.router.match(location, this.current)
  this.confirmTransition(route, () => {
    this.updateRoute(route)
    // ...其他逻辑
  }, onAbort)
}

match方法通过路径匹配找到对应的路由记录,并提取参数:

// [src/index.js](https://link.gitcode.com/i/ce6502334bdf2362b678c0d907da1a18)
match (
  raw: RawLocation,
  current?: Route,
  redirectedFrom?: Location
): Route {
  const location = normalizeLocation(raw, current, false, this)
  const { name } = location

  if (name) {
    // 按名称匹配逻辑
  } else if (location.path) {
    // 按路径匹配逻辑
    location.params = {}
    for (let i = 0; i < pathList.length; i++) {
      const path = pathList[i]
      const record = pathMap[path]
      if (match = record.regex.exec(location.path)) {
        // 提取参数
        const params: Object = {}
        for (let i = 1; i < match.length; i++) {
          const key = record.keys[i - 1]
          const val = match[i] !== undefined ? decodeURIComponent(match[i]) : undefined
          params[key.name] = val
        }
        location.params = params
        return _createRoute(record, location, redirectedFrom)
      }
    }
  }
}

3. Query参数的处理逻辑

Query参数的解析相对简单,使用querystring库解析URL中的查询字符串部分:

// [src/util/query.js](https://link.gitcode.com/i/e379125d5b948ea7a9b092e340af1be5)
export function parseQuery (query: string): Object {
  const res = {}
  query = query.trim().replace(/^(\?|#|&)/, '')
  
  if (!query) {
    return res
  }
  
  query.split('&').forEach(param => {
    const parts = param.replace(/\+/g, ' ').split('=')
    const key = decodeURIComponent(parts.shift())
    const val = parts.length > 0 
      ? decodeURIComponent(parts.join('=')) 
      : null
    
    if (res[key] === undefined) {
      res[key] = val
    } else if (Array.isArray(res[key])) {
      res[key].push(val)
    } else {
      res[key] = [res[key], val]
    }
  })
  
  return res
}

参数在组件中的获取方式

1. 直接访问$route对象

组件中可通过this.$route.paramsthis.$route.query直接访问参数:

// 组件内部
export default {
  mounted() {
    console.log(this.$route.params.id) // 动态参数
    console.log(this.$route.query.page) // 查询参数
  }
}

$route是通过Vue原型注入的响应式对象:

// [src/install.js](https://link.gitcode.com/i/93d04991bc5c19023fa0b5d606e7a724)
Object.defineProperty(Vue.prototype, '$route', {
  get () { return this._routerRoot._route }
})

2. 使用路由 props 解耦

为避免组件与$route强耦合,可使用props选项将参数注入组件:

// 路由配置
const routes = [
  { 
    path: '/user/:id', 
    component: User,
    props: true // 将params转换为组件props
  },
  {
    path: '/search',
    component: Search,
    // 函数模式,可处理query参数
    props: (route) => ({ 
      keyword: route.query.keyword,
      page: route.query.page || 1
    })
  }
]

// 组件中直接使用props
export default {
  props: ['id', 'keyword', 'page'],
  mounted() {
    console.log(this.id) // 来自params
    console.log(this.keyword) // 来自query
  }
}

参数变化的检测与响应

1. 监听$route对象

当路由参数变化但组件被复用时(如从/user/1/user/2),组件不会重新创建,需通过watch监听参数变化:

export default {
  watch: {
    '$route'(to, from) {
      // 处理参数变化逻辑
      this.loadUser(to.params.id)
    },
    // 或只监听特定参数
    '$route.params.id'(id) {
      this.loadUser(id)
    }
  }
}

2. 使用beforeRouteUpdate导航守卫

组件内导航守卫也可检测参数变化:

export default {
  beforeRouteUpdate(to, from, next) {
    // 在导航完成前获取参数
    this.id = to.params.id
    this.loadUser(this.id)
    next()
  }
}

源码中的参数安全处理

Vue-Router 对参数进行了多方面的安全处理:

  1. URL编码/解码:参数在存储和传递过程中会进行URI编码,避免特殊字符问题
// [src/util/query.js](https://link.gitcode.com/i/e379125d5b948ea7a9b092e340af1be5)
function encodeQuery (obj: Dictionary<any>): string {
  const parts: string[] = []
  for (const key in obj) {
    const val = obj[key]
    if (val === undefined) return ''
    if (val === null) continue
    if (Array.isArray(val)) {
      val.forEach(v => {
        parts.push(encode(key) + '=' + encode(v))
      })
    } else {
      parts.push(encode(key) + '=' + encode(val))
    }
  }
  return parts.join('&')
}
  1. 重复导航检测:避免相同参数的重复导航
// [src/history/base.js](https://link.gitcode.com/i/32e8e625c2c0ea7ab0f4cd6f0192b032)
confirmTransition (route: Route, onComplete: Function, onAbort?: Function) {
  const current = this.current
  if (
    isSameRoute(route, current) &&
    route.matched.length === current.matched.length
  ) {
    this.ensureURL()
    return abort()
  }
  // ...其他逻辑
}

最佳实践与性能优化

  1. 参数使用建议

    • 资源标识用Params(如/user/:id
    • 筛选条件用Query(如/list?page=1&sort=desc
    • 复杂状态考虑使用Vuex管理而非URL参数
  2. 性能优化

    • 使用props解耦路由参数,提高组件复用性
    • 对频繁变化的参数使用v-model配合$router.push而非直接修改$route
    • 合理使用keep-alive缓存组件时,注意参数变化的监听
  3. 安全注意事项

    • 避免在URL中传递敏感信息
    • 服务端渲染(SSR)时需对参数进行验证和过滤

总结

Vue-Router 通过路径正则匹配和查询字符串解析,实现了灵活的路由参数处理机制。核心流程包括:

  1. 路由配置转换为匹配规则(含参数定义)
  2. 路径匹配时提取Params和Query参数
  3. 通过$route对象或props注入组件
  4. 提供参数变化检测机制

理解参数传递的源码实现,有助于开发者在复杂场景下选择合适的参数传递方式,并编写更健壮的路由逻辑。完整的路由参数处理源码可参考:

【免费下载链接】vue-analysis :thumbsup: Vue.js 源码分析 【免费下载链接】vue-analysis 项目地址: https://gitcode.com/gh_mirrors/vu/vue-analysis

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值