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 onlyPerformance 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
}
}🔗 Related APIs
RouteRecordRaw- Raw route configuration typeRouteRecordNormalized- Normalized route record interfaceRouter.getRoutes()- Method to get all route records
📚 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
RouteRecordfor advanced route analysis and permission systems. For most application code, stick to using the router's navigation methods and the current route information fromuseRoute().