Skip to content

routeLocationKey - 路由位置键

routeLocationKey 是一个Symbol类型的常量,用于在 Vue 的依赖注入系统中标识当前的路由位置信息。它是访问路由状态的核心注入键之一。📍

📋 变量定义

typescript
const routeLocationKey: InjectionKey<RouteLocationNormalized>

🎯 功能说明

这个变量提供了对当前路由位置的完整访问能力:

  1. 依赖注入键 - 用于在组件树中注入当前路由位置
  2. 完整路由信息 - 包含路径、参数、查询、哈希等所有路由数据
  3. 类型安全 - 提供完整的 TypeScript 类型支持

🔧 技术细节

类型定义

typescript
// routeLocationKey 的完整类型
const routeLocationKey: InjectionKey<RouteLocationNormalized>

// 对应的注入值类型(RouteLocationNormalized)
interface RouteLocationNormalized {
  path: string                    // 路径
  fullPath: string                // 完整路径(包含查询和哈希)
  params: RouteParams             // 路径参数
  query: LocationQuery            // 查询参数
  hash: string                    // 哈希值
  name?: RouteRecordName          // 路由名称
  matched: RouteRecordNormalized[] // 匹配的路由记录
  meta: RouteMeta                 // 路由元数据
  redirectedFrom?: RouteLocation   // 重定向来源
}

在 Vue Router 中的使用

typescript
// Vue Router 内部组件使用示例
export default defineComponent({
  inject: {
    routeLocation: { from: routeLocationKey }
  },
  
  setup(props, { injections }) {
    const route = injections.routeLocation as RouteLocationNormalized
    // 使用当前路由位置信息
  }
})

💡 实际应用示例

基础路由信息访问

vue
<script setup>
import { routeLocationKey } from 'vue-router'
import { inject, computed } from 'vue'

// 注入当前路由位置
const routeLocation = inject(routeLocationKey)

// 计算常用的路由信息
const routeInfo = computed(() => ({
  currentPath: routeLocation?.path,
  fullPath: routeLocation?.fullPath,
  params: routeLocation?.params || {},
  query: routeLocation?.query || {},
  hash: routeLocation?.hash,
  routeName: routeLocation?.name
}))

// 检查特定条件
const isProductPage = computed(() => 
  routeLocation?.path?.startsWith('/product/')
)

const hasSearchQuery = computed(() => 
  routeLocation?.query?.q !== undefined
)
</script>

<template>
  <div class="route-info">
    <h4>当前路由信息</h4>
    <p>路径: {{ routeInfo.currentPath }}</p>
    <p>完整路径: {{ routeInfo.fullPath }}</p>
    <p>参数: {{ JSON.stringify(routeInfo.params) }}</p>
    <p>查询: {{ JSON.stringify(routeInfo.query) }}</p>
    
    <div v-if="isProductPage" class="product-badge">
      产品页面
    </div>
  </div>
</template>

高级路由监控组件

vue
<script setup>
import { routeLocationKey } from 'vue-router'
import { inject, watch, ref } from 'vue'

const routeLocation = inject(routeLocationKey)

// 路由变化历史记录
const routeHistory = ref([])

// 监听路由变化
watch(routeLocation, (newRoute, oldRoute) => {
  if (newRoute && oldRoute) {
    routeHistory.value.push({
      from: oldRoute.path,
      to: newRoute.path,
      timestamp: new Date().toISOString(),
      queryChanged: newRoute.query !== oldRoute.query,
      paramsChanged: newRoute.params !== oldRoute.params
    })
    
    // 保持最近10条记录
    if (routeHistory.value.length > 10) {
      routeHistory.value.shift()
    }
  }
}, { immediate: true })

// 路由统计信息
const routeStats = ref({
  totalVisits: 0,
  uniquePaths: new Set(),
  queryUsage: {}
})

// 更新统计信息
watch(routeLocation, (newRoute) => {
  if (newRoute) {
    routeStats.value.totalVisits++
    routeStats.value.uniquePaths.add(newRoute.path)
    
    // 统计查询参数使用情况
    Object.keys(newRoute.query).forEach(key => {
      routeStats.value.queryUsage[key] = 
        (routeStats.value.queryUsage[key] || 0) + 1
    })
  }
})
</script>

<template>
  <div class="route-monitor">
    <h4>路由监控</h4>
    
    <div class="stats">
      <p>总访问: {{ routeStats.totalVisits }}</p>
      <p>唯一路径: {{ routeStats.uniquePaths.size }}</p>
    </div>
    
    <div class="history">
      <h5>最近路由变化</h5>
      <div 
        v-for="(record, index) in routeHistory" 
        :key="index"
        class="history-item"
      >
        {{ record.from }} → {{ record.to }}
      </div>
    </div>
  </div>
</template>

路由感知的表单组件

vue
<script setup>
import { routeLocationKey } from 'vue-router'
import { inject, computed, watch } from 'vue'

const routeLocation = inject(routeLocationKey)

// 从路由参数初始化表单数据
const formData = ref({
  category: routeLocation?.params.category || '',
  search: routeLocation?.query.q || '',
  page: Number(routeLocation?.query.page) || 1,
  sort: routeLocation?.query.sort || 'name'
})

// 当路由变化时更新表单数据
watch(routeLocation, (newRoute) => {
  if (newRoute) {
    formData.value = {
      category: newRoute.params.category || '',
      search: newRoute.query.q || '',
      page: Number(newRoute.query.page) || 1,
      sort: newRoute.query.sort || 'name'
    }
  }
})

// 当表单数据变化时更新路由
watch(formData, (newData, oldData) => {
  if (JSON.stringify(newData) !== JSON.stringify(oldData)) {
    // 构建新的查询参数
    const query = {
      ...routeLocation?.query,
      q: newData.search || undefined,
      page: newData.page > 1 ? newData.page.toString() : undefined,
      sort: newData.sort !== 'name' ? newData.sort : undefined
    }
    
    // 清理空值
    Object.keys(query).forEach(key => {
      if (query[key] === undefined || query[key] === '') {
        delete query[key]
      }
    })
    
    // 更新路由(不触发组件重新挂载)
    router.push({
      path: routeLocation?.path,
      query
    })
  }
}, { deep: true, immediate: false })
</script>

<template>
  <form class="search-form">
    <input 
      v-model="formData.search" 
      placeholder="搜索..."
      type="search"
    />
    
    <select v-model="formData.category">
      <option value="">所有分类</option>
      <option value="electronics">电子产品</option>
      <option value="books">图书</option>
    </select>
    
    <select v-model="formData.sort">
      <option value="name">按名称</option>
      <option value="price">按价格</option>
      <option value="date">按日期</option>
    </select>
    
    <button type="submit">搜索</button>
  </form>
</template>

🎯 使用场景

面包屑导航组件

vue
<script setup>
import { routeLocationKey } from 'vue-router'
import { inject, computed } from 'vue'

const routeLocation = inject(routeLocationKey)

// 生成面包屑导航数据
const breadcrumbs = computed(() => {
  if (!routeLocation?.matched) return []
  
  return routeLocation.matched.map((route, index) => {
    const isLast = index === routeLocation.matched.length - 1
    
    return {
      name: route.meta?.breadcrumb || route.name || route.path,
      path: isLast ? null : generatePath(route, routeLocation.params),
      active: isLast
    }
  })
})

// 生成路径(处理动态参数)
function generatePath(route, params) {
  let path = route.path
  
  // 替换路径参数
  Object.keys(params).forEach(key => {
    path = path.replace(`:${key}`, params[key])
  })
  
  return path
}
</script>

<template>
  <nav class="breadcrumb">
    <router-link 
      v-for="(crumb, index) in breadcrumbs" 
      :key="index"
      :to="crumb.path"
      :class="{ active: crumb.active }"
      class="breadcrumb-item"
    >
      {{ crumb.name }}
      <span v-if="!crumb.active" class="separator">/</span>
    </router-link>
  </nav>
</template>

页面标题管理

vue
<script setup>
import { routeLocationKey } from 'vue-router'
import { inject, watch } from 'vue'

const routeLocation = inject(routeLocationKey)

// 根据路由动态设置页面标题
watch(routeLocation, (newRoute) => {
  if (newRoute) {
    const titleParts = []
    
    // 添加路由元数据中的标题
    if (newRoute.meta?.title) {
      titleParts.push(newRoute.meta.title)
    }
    
    // 添加路由名称(如果没有元数据标题)
    else if (newRoute.name) {
      titleParts.push(newRoute.name)
    }
    
    // 添加站点名称
    titleParts.push('我的应用')
    
    // 设置文档标题
    document.title = titleParts.join(' - ')
  }
}, { immediate: true })
</script>

<template>
  <!-- 这个组件不需要模板,它只负责设置页面标题 -->
</template>

🔧 实用工具函数

路由信息工具

typescript
// 提供便捷的路由信息访问
export function useRouteLocation() {
  const routeLocation = inject(routeLocationKey)
  
  return {
    // 基础信息
    path: routeLocation?.path,
    fullPath: routeLocation?.fullPath,
    name: routeLocation?.name,
    
    // 参数访问
    params: routeLocation?.params || {},
    query: routeLocation?.query || {},
    hash: routeLocation?.hash,
    
    // 便捷方法
    hasParam: (key: string) => key in (routeLocation?.params || {}),
    getParam: (key: string) => routeLocation?.params?.[key],
    hasQuery: (key: string) => key in (routeLocation?.query || {}),
    getQuery: (key: string) => routeLocation?.query?.[key],
    
    // 验证方法
    isCurrentRoute: (path: string) => routeLocation?.path === path,
    matchesPattern: (pattern: RegExp) => pattern.test(routeLocation?.path || '')
  }
}

// 使用示例
const { path, hasQuery, getQuery } = useRouteLocation()

🔗 相关变量

💡 专业建议:虽然可以直接使用 routeLocationKey 进行依赖注入,但在大多数情况下,使用 useRoute() 组合式 API 是更简单和推荐的方式,它提供了相同的功能但更易于使用。

🚀 Vue Router - 让前端路由变得简单而强大 | 构建现代化单页应用的最佳选择