Skip to content

RouteRecord Type Alias | Vue Router

RouteRecord is a type alias that represents a normalized route record after it has been processed by the router. It contains comprehensive information about a route's configuration and matching capabilities. 📋

📋 Type Definition

typescript
type RouteRecord = RouteRecordNormalized

🎯 Purpose and Usage

The RouteRecord type alias provides access to normalized route information after the router has processed the raw route configuration. It's used throughout Vue Router for route matching, navigation, and route analysis.

Key Characteristics:

  • Normalized Format - Consistent structure after processing
  • Complete Information - Contains all route configuration details
  • Matching Ready - Optimized for route matching operations
  • Type Safety - Full TypeScript support with proper typing

🔍 Type Structure

The RouteRecord type is an alias for RouteRecordNormalized, which includes:

Core Route Information

typescript
interface RouteRecordNormalized {
  // Identification
  readonly path: string
  readonly name?: RouteRecordName
  readonly redirect?: RouteRecordRedirectOption
  
  // Component configuration
  readonly components: Record<string, Component>
  readonly children: RouteRecordNormalized[]
  
  // Meta information
  readonly meta: RouteMeta
  readonly props: Record<string, any>
  
  // Matching configuration
  readonly alias: string[]
  readonly beforeEnter?: NavigationGuard
  
  // Additional properties
  readonly instance?: ComponentPublicInstance
  readonly leaveGuards: Set<NavigationGuard>
  // ... other internal properties
}

💡 Practical Usage Examples

Accessing Route Information

typescript
import type { RouteRecord } from 'vue-router'

// Get all registered routes
const routes: RouteRecord[] = router.getRoutes()

// Find specific route by name
const userRoute = routes.find(route => route.name === 'user-profile')

// Access route properties
if (userRoute) {
  console.log('Route path:', userRoute.path)
  console.log('Route name:', userRoute.name)
  console.log('Route meta:', userRoute.meta)
  console.log('Route components:', userRoute.components)
}

Route Analysis and Utilities

typescript
class RouteAnalyzer {
  // Analyze route structure
  static analyzeRoute(route: RouteRecord): RouteAnalysis {
    return {
      path: route.path,
      name: route.name,
      hasChildren: route.children.length > 0,
      childCount: route.children.length,
      hasDynamicSegments: route.path.includes(':'),
      metaKeys: Object.keys(route.meta),
      componentCount: Object.keys(route.components).length,
      hasAliases: route.alias.length > 0
    }
  }
  
  // Find routes matching pattern
  static findRoutesByPattern(routes: RouteRecord[], pattern: string): RouteRecord[] {
    return routes.filter(route => 
      route.path.includes(pattern) || 
      route.name?.toString().includes(pattern)
    )
  }
  
  // Get all route paths
  static getAllPaths(routes: RouteRecord[]): string[] {
    return routes.map(route => route.path)
  }
  
  // Check if route requires authentication
  static requiresAuth(route: RouteRecord): boolean {
    return route.meta.requiresAuth === true
  }
}

Route-Based Feature Detection

typescript
class RouteFeatureDetector {
  // Check if route has specific feature
  static hasFeature(route: RouteRecord, feature: string): boolean {
    const features = route.meta.features as string[] | undefined
    return features ? features.includes(feature) : false
  }
  
  // Get routes with specific meta property
  static getRoutesWithMeta(routes: RouteRecord[], metaKey: string): RouteRecord[] {
    return routes.filter(route => metaKey in route.meta)
  }
  
  // Find routes by component
  static findRoutesByComponent(routes: RouteRecord[], component: Component): RouteRecord[] {
    return routes.filter(route => 
      Object.values(route.components).includes(component)
    )
  }
}

🔧 Advanced Patterns

Dynamic Route Configuration Analysis

typescript
class RouteConfigurationAnalyzer {
  private routes: RouteRecord[]
  
  constructor(routes: RouteRecord[]) {
    this.routes = routes
  }
  
  // Analyze route hierarchy
  analyzeHierarchy(): RouteHierarchy {
    const rootRoutes = this.routes.filter(route => 
      !this.isChildRoute(route)
    )
    
    return {
      rootCount: rootRoutes.length,
      totalRoutes: this.routes.length,
      maxDepth: this.getMaxDepth(),
      averageChildren: this.getAverageChildren(),
      routeDistribution: this.getRouteDistribution()
    }
  }
  
  private isChildRoute(route: RouteRecord): boolean {
    return this.routes.some(parent => 
      parent.children.includes(route as any)
    )
  }
  
  private getMaxDepth(): number {
    let maxDepth = 0
    
    const calculateDepth = (route: RouteRecord, depth: number = 0) => {
      maxDepth = Math.max(maxDepth, depth)
      route.children.forEach(child => 
        calculateDepth(child as RouteRecord, depth + 1)
      )
    }
    
    this.routes.forEach(route => calculateDepth(route))
    return maxDepth
  }
  
  // Generate route map for documentation
  generateRouteMap(): RouteMap {
    const map: RouteMap = {}
    
    this.routes.forEach(route => {
      map[route.path] = {
        name: route.name,
        components: Object.keys(route.components),
        meta: route.meta,
        children: route.children.map(child => ({
          path: child.path,
          name: child.name
        }))
      }
    })
    
    return map
  }
}

Route Permission System

typescript
class RoutePermissionSystem {
  private userPermissions: Set<string> = new Set()
  
  // Check if user can access route
  canAccessRoute(route: RouteRecord, user: User): boolean {
    // Check authentication requirement
    if (route.meta.requiresAuth && !user.isAuthenticated) {
      return false
    }
    
    // Check role-based access
    const requiredRoles = route.meta.requiredRoles as string[] | undefined
    if (requiredRoles && requiredRoles.length > 0) {
      const hasRequiredRole = requiredRoles.some(role =>
        user.roles.includes(role)
      )
      if (!hasRequiredRole) return false
    }
    
    // Check feature flags
    const requiredFeatures = route.meta.features as string[] | undefined
    if (requiredFeatures && requiredFeatures.length > 0) {
      const hasFeatures = requiredFeatures.every(feature =>
        this.userPermissions.has(feature)
      )
      if (!hasFeatures) return false
    }
    
    return true
  }
  
  // Get accessible routes for user
  getAccessibleRoutes(user: User): RouteRecord[] {
    return router.getRoutes().filter(route =>
      this.canAccessRoute(route, user)
    )
  }
  
  // Generate navigation menu based on permissions
  generateNavigationMenu(user: User): NavigationItem[] {
    return router.getRoutes()
      .filter(route => this.canAccessRoute(route, user))
      .filter(route => route.meta.showInNavigation !== false)
      .map(route => ({
        path: route.path,
        name: route.meta.title as string || String(route.name || route.path),
        icon: route.meta.icon as string,
        children: route.children
          .filter(child => this.canAccessRoute(child as RouteRecord, user))
          .map(child => ({
            path: child.path,
            name: child.meta.title as string || String(child.name || child.path)
          }))
      }))
  }
}

Route Testing Utilities

typescript
class RouteTestingUtils {
  // Test route matching
  static testRouteMatching(route: RouteRecord, testPaths: string[]): MatchingResult[] {
    return testPaths.map(testPath => {
      const match = router.resolve(testPath)
      const isMatch = match.matched.some(matchedRoute => 
        matchedRoute.path === route.path
      )
      
      return {
        testPath,
        routePath: route.path,
        isMatch,
        matchedRoute: isMatch ? match.matched[0] : null,
        params: match.params
      }
    })
  }
  
  // Validate route configuration
  static validateRouteConfig(route: RouteRecord): ValidationResult {
    const errors: string[] = []
    
    // Check path format
    if (!route.path.startsWith('/')) {
      errors.push('Route path must start with /')
    }
    
    // Check component existence
    if (Object.keys(route.components).length === 0) {
      errors.push('Route must have at least one component')
    }
    
    // Check for circular references in children
    if (this.hasCircularReference(route)) {
      errors.push('Route has circular reference in children')
    }
    
    return { isValid: errors.length === 0, errors }
  }
  
  private static hasCircularReference(route: RouteRecord, visited: Set<string> = new Set()): boolean {
    if (visited.has(route.path)) return true
    visited.add(route.path)
    
    for (const child of route.children) {
      if (this.hasCircularReference(child as RouteRecord, visited)) {
        return true
      }
    }
    
    visited.delete(route.path)
    return false
  }
}

🚨 Important Considerations

Route Record vs Route Record Raw

typescript
// RouteRecord (normalized) - After router processing
const normalizedRoute: RouteRecord = router.getRoutes()[0]

// RouteRecordRaw (raw configuration) - Before processing  
const rawRoute: RouteRecordRaw = {
  path: '/user/:id',
  component: UserComponent,
  meta: { requiresAuth: true }
}

// Key differences:
// - RouteRecord has normalized properties
// - RouteRecord has additional internal properties
// - RouteRecord is ready for matching
// - RouteRecordRaw is for configuration only

Performance Considerations

typescript
// ⚠️ Getting all routes can be expensive for large applications
const allRoutes = router.getRoutes() // Can be slow with many routes

// ✅ Better: Cache route information when possible
class RouteCache {
  private cachedRoutes: RouteRecord[] | null = null
  
  getRoutes(): RouteRecord[] {
    if (!this.cachedRoutes) {
      this.cachedRoutes = router.getRoutes()
    }
    return this.cachedRoutes
  }
  
  invalidateCache() {
    this.cachedRoutes = null
  }
}

📚 Best Practices

Route Information Caching

typescript
class RouteInformationCache {
  private cache = new Map<string, any>()
  private cacheTimeout = 30000 // 30 seconds
  
  getRouteInfo(route: RouteRecord, key: string): any {
    const cacheKey = `${route.path}-${key}`
    const cached = this.cache.get(cacheKey)
    
    if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
      return cached.value
    }
    
    // Calculate fresh value
    const value = this.calculateRouteInfo(route, key)
    this.cache.set(cacheKey, { value, timestamp: Date.now() })
    return value
  }
  
  private calculateRouteInfo(route: RouteRecord, key: string): any {
    switch (key) {
      case 'analysis':
        return RouteAnalyzer.analyzeRoute(route)
      case 'permissions':
        return this.extractPermissions(route)
      case 'breadcrumbs':
        return this.generateBreadcrumbs(route)
      default:
        return null
    }
  }
}

Route Documentation Generation

typescript
class RouteDocumentationGenerator {
  generateAPIDocumentation(routes: RouteRecord[]): APIDocumentation {
    return {
      version: '1.0.0',
      generatedAt: new Date().toISOString(),
      routes: routes.map(route => ({
        path: route.path,
        name: route.name,
        method: 'GET', // Assuming all are GET for SPA
        description: route.meta.description as string || '',
        parameters: this.extractParameters(route),
        responses: this.generateResponses(route),
        examples: this.generateExamples(route)
      }))
    }
  }
  
  private extractParameters(route: RouteRecord): Parameter[] {
    const parameters: Parameter[] = []
    const paramMatches = route.path.match(/:\w+/g) || []
    
    paramMatches.forEach(param => {
      const name = param.slice(1) // Remove :
      parameters.push({
        name,
        type: 'string',
        required: !route.path.includes(`:${name}?`),
        description: `Dynamic parameter: ${name}`
      })
    })
    
    return parameters
  }
}

💡 Pro Tip: Use RouteRecord for advanced route analysis and permission systems. For most application code, stick to using the router's navigation methods and the current route information from useRoute().

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