Skip to content

createRouterMatcher Function | Vue Router

createRouterMatcher is a low-level function that creates a route matcher instance for matching URL paths against route configurations. This function is primarily used internally by Vue Router but can be useful for advanced routing scenarios. 🎯

📋 Function Signature

typescript
function createRouterMatcher(
  routes: RouteRecordRaw[], 
  globalOptions?: PathParserOptions
): RouterMatcher

🎯 Purpose and Usage

The createRouterMatcher function creates a specialized matcher that can efficiently match URL paths against your route configuration. While most applications use the router directly, this function provides access to the underlying matching logic for advanced use cases.

Key Use Cases:

  • Custom Routing Logic - Build custom routing systems
  • Route Analysis - Analyze route matching without navigation
  • Performance Optimization - Pre-compile routes for faster matching
  • Testing - Test route matching in isolation

🔍 Parameters

routes

  • Type: RouteRecordRaw[]
  • Description: Array of route records to match against

The routes parameter accepts the same route configuration format as the main router.

typescript
const routes: RouteRecordRaw[] = [
  { path: '/', component: Home },
  { path: '/user/:id', component: UserProfile },
  { path: '/blog/:year/:month', component: BlogArchive }
]

globalOptions (optional)

  • Type: PathParserOptions
  • Description: Global options for path parsing

Options for customizing how paths are parsed and matched.

typescript
const options: PathParserOptions = {
  sensitive: false,    // Case sensitivity
  strict: false,       // Trailing slash handling
  end: true,           // Match full path
  start: true          // Match from start
}

💡 Practical Usage Examples

Basic Route Matcher Setup

typescript
import { createRouterMatcher } from 'vue-router'

const routes = [
  { path: '/', name: 'home' },
  { path: '/user/:id', name: 'user' },
  { path: '/product/:category/:id', name: 'product' }
]

const matcher = createRouterMatcher(routes)

// Match paths against routes
const match1 = matcher.resolve('/')
console.log(match1) // { matched: [...], params: {}, path: '/' }

const match2 = matcher.resolve('/user/123')
console.log(match2) // { matched: [...], params: { id: '123' }, path: '/user/123' }

Advanced Route Analysis

typescript
class RouteAnalyzer {
  private matcher: RouterMatcher
  
  constructor(routes: RouteRecordRaw[]) {
    this.matcher = createRouterMatcher(routes)
  }
  
  // Analyze which routes match a given path
  analyzePath(path: string): RouteAnalysis {
    const match = this.matcher.resolve(path)
    
    return {
      path,
      matches: match.matched.length > 0,
      matchedRoutes: match.matched.map(record => record.path),
      parameters: match.params,
      isExactMatch: this.isExactMatch(match, path)
    }
  }
  
  // Find all routes that could match a pattern
  findPotentialMatches(partialPath: string): RouteRecordRaw[] {
    const allRoutes = this.getAllRoutes()
    return allRoutes.filter(route => 
      this.couldMatch(route.path, partialPath)
    )
  }
  
  private isExactMatch(match: any, path: string): boolean {
    return match.matched.some(record => 
      record.path === path || this.normalizePath(record.path) === this.normalizePath(path)
    )
  }
}

Custom Routing System

typescript
class CustomRouter {
  private matcher: RouterMatcher
  private currentRoute: RouteLocationNormalized
  
  constructor(routes: RouteRecordRaw[]) {
    this.matcher = createRouterMatcher(routes)
    this.currentRoute = this.getInitialRoute()
  }
  
  // Custom navigation method
  async navigateTo(path: string, options?: NavigationOptions): Promise<boolean> {
    const match = this.matcher.resolve(path)
    
    if (match.matched.length === 0) {
      throw new Error(`No route found for path: ${path}`)
    }
    
    // Custom pre-navigation logic
    if (options?.preNavigationHook) {
      const shouldProceed = await options.preNavigationHook(this.currentRoute, match)
      if (!shouldProceed) return false
    }
    
    // Update current route
    this.currentRoute = this.createRouteLocation(match)
    
    // Trigger navigation events
    this.emit('navigation', this.currentRoute)
    
    return true
  }
  
  // Get route for path without navigating
  getRouteForPath(path: string): RouteLocationNormalized | null {
    const match = this.matcher.resolve(path)
    return match.matched.length > 0 ? this.createRouteLocation(match) : null
  }
}

🔧 Advanced Patterns

Route Precompilation for Performance

typescript
class OptimizedRouteMatcher {
  private compiledMatchers: Map<string, RouterMatcher> = new Map()
  
  precompileRoutes(routes: RouteRecordRaw[], key: string): void {
    const matcher = createRouterMatcher(routes, {
      sensitive: true,
      strict: true
    })
    this.compiledMatchers.set(key, matcher)
  }
  
  matchWithPrecompiled(key: string, path: string): any {
    const matcher = this.compiledMatchers.get(key)
    if (!matcher) {
      throw new Error(`No precompiled matcher found for key: ${key}`)
    }
    return matcher.resolve(path)
  }
  
  // Batch matching for multiple paths
  batchMatch(paths: string[], key: string): Map<string, any> {
    const results = new Map()
    const matcher = this.compiledMatchers.get(key)
    
    if (!matcher) return results
    
    for (const path of paths) {
      results.set(path, matcher.resolve(path))
    }
    
    return results
  }
}

Dynamic Route Configuration

typescript
class DynamicRouteManager {
  private baseMatcher: RouterMatcher
  private dynamicRoutes: RouteRecordRaw[] = []
  
  constructor(baseRoutes: RouteRecordRaw[]) {
    this.baseMatcher = createRouterMatcher(baseRoutes)
  }
  
  // Add routes dynamically
  addDynamicRoute(route: RouteRecordRaw): void {
    this.dynamicRoutes.push(route)
    // Recreate matcher with updated routes
    this.baseMatcher = createRouterMatcher([
      ...this.getBaseRoutes(),
      ...this.dynamicRoutes
    ])
  }
  
  // Remove dynamic route
  removeDynamicRoute(path: string): boolean {
    const index = this.dynamicRoutes.findIndex(route => route.path === path)
    if (index !== -1) {
      this.dynamicRoutes.splice(index, 1)
      this.baseMatcher = createRouterMatcher([
        ...this.getBaseRoutes(),
        ...this.dynamicRoutes
      ])
      return true
    }
    return false
  }
  
  // Check if path matches any dynamic route
  isDynamicRoute(path: string): boolean {
    const match = this.baseMatcher.resolve(path)
    return this.dynamicRoutes.some(route => 
      match.matched.some(matchedRoute => matchedRoute.path === route.path)
    )
  }
}

Route Testing Utilities

typescript
class RouteTestingUtils {
  static createTestMatcher(routes: RouteRecordRaw[]): RouterMatcher {
    return createRouterMatcher(routes, {
      sensitive: false,
      strict: false
    })
  }
  
  // Test route parameter extraction
  static testRouteParameters(routePath: string, actualPath: string): Record<string, string> {
    const matcher = this.createTestMatcher([{ path: routePath }])
    const match = matcher.resolve(actualPath)
    return match.params
  }
  
  // Test route matching with different options
  static testMatchingOptions(
    routePath: string, 
    testPath: string, 
    options: PathParserOptions
  ): boolean {
    const matcher = createRouterMatcher([{ path: routePath }], options)
    const match = matcher.resolve(testPath)
    return match.matched.length > 0
  }
  
  // Generate test cases for route matching
  static generateRouteTestCases(routes: RouteRecordRaw[]): TestCase[] {
    const matcher = createRouterMatcher(routes)
    const testCases: TestCase[] = []
    
    for (const route of routes) {
      if (route.path.includes(':')) {
        // Generate test paths for parameterized routes
        const testPaths = this.generateParameterizedTestPaths(route.path)
        testPaths.forEach(testPath => {
          const match = matcher.resolve(testPath)
          testCases.push({
            routePath: route.path,
            testPath,
            shouldMatch: match.matched.length > 0,
            expectedParams: match.params
          })
        })
      } else {
        // Test exact path matching
        const match = matcher.resolve(route.path)
        testCases.push({
          routePath: route.path,
          testPath: route.path,
          shouldMatch: true,
          expectedParams: {}
        })
      }
    }
    
    return testCases
  }
}

🚨 Important Considerations

Performance Implications

typescript
// ⚠️ Creating matchers is computationally expensive
// Avoid creating matchers frequently in performance-critical code

// ❌ Don't do this in loops or frequent operations
function badPattern(path: string) {
  const matcher = createRouterMatcher(routes) // Expensive!
  return matcher.resolve(path)
}

// ✅ Better: Create once, reuse often
class EfficientMatcher {
  private matcher: RouterMatcher
  
  constructor(routes: RouteRecordRaw[]) {
    this.matcher = createRouterMatcher(routes) // Create once
  }
  
  matchPath(path: string) {
    return this.matcher.resolve(path) // Reuse
  }
}

Memory Management

typescript
// Matchers can hold references to routes and configurations
// Be mindful of memory usage with large route configurations

class MemoryAwareMatcher {
  private matcher: RouterMatcher | null = null
  private routes: RouteRecordRaw[] = []
  
  setRoutes(newRoutes: RouteRecordRaw[]): void {
    this.routes = newRoutes
    // Clear old matcher to free memory
    this.matcher = null
  }
  
  getMatcher(): RouterMatcher {
    if (!this.matcher) {
      this.matcher = createRouterMatcher(this.routes)
    }
    return this.matcher
  }
  
  // Clean up when no longer needed
  dispose(): void {
    this.matcher = null
    this.routes = []
  }
}

📚 Best Practices

Custom Route Resolution

typescript
class AdvancedRouteResolver {
  private matcher: RouterMatcher
  private customResolvers: Map<string, CustomResolver> = new Map()
  
  constructor(routes: RouteRecordRaw[]) {
    this.matcher = createRouterMatcher(routes)
  }
  
  // Add custom resolver for specific route patterns
  addResolver(pattern: string, resolver: CustomResolver): void {
    this.customResolvers.set(pattern, resolver)
  }
  
  // Enhanced route resolution with custom logic
  async resolveWithCustomLogic(path: string, context: ResolutionContext): Promise<EnhancedRouteMatch> {
    const basicMatch = this.matcher.resolve(path)
    
    // Apply custom resolvers
    for (const [pattern, resolver] of this.customResolvers) {
      if (this.matchesPattern(path, pattern)) {
        const enhancedData = await resolver.resolve(basicMatch, context)
        return { ...basicMatch, ...enhancedData }
      }
    }
    
    return basicMatch
  }
  
  private matchesPattern(path: string, pattern: string): boolean {
    const patternMatcher = createRouterMatcher([{ path: pattern }])
    return patternMatcher.resolve(path).matched.length > 0
  }
}

Route Validation and Sanitization

typescript
class RouteValidator {
  private matcher: RouterMatcher
  
  constructor(routes: RouteRecordRaw[]) {
    this.matcher = createRouterMatcher(routes)
  }
  
  // Validate route configuration
  validateRoutes(): ValidationResult {
    const issues: string[] = []
    
    // Check for duplicate paths
    const pathCounts = new Map()
    this.getAllPaths().forEach(path => {
      pathCounts.set(path, (pathCounts.get(path) || 0) + 1)
    })
    
    pathCounts.forEach((count, path) => {
      if (count > 1) {
        issues.push(`Duplicate path found: ${path}`)
      }
    })
    
    // Check parameter consistency
    this.checkParameterConsistency(issues)
    
    return { isValid: issues.length === 0, issues }
  }
  
  // Sanitize user-provided paths
  sanitizePath(userPath: string): string {
    // Remove potentially dangerous characters
    let sanitized = userPath.replace(/[<>"']/g, '')
    
    // Ensure proper formatting
    if (!sanitized.startsWith('/')) {
      sanitized = '/' + sanitized
    }
    
    // Test if sanitized path matches any route
    const match = this.matcher.resolve(sanitized)
    if (match.matched.length === 0) {
      // Fallback to home if no match
      return '/'
    }
    
    return sanitized
  }
}

💡 Pro Tip: While createRouterMatcher is powerful for advanced routing scenarios, most applications should use the standard Vue Router API. Reserve this function for cases where you need fine-grained control over route matching or are building custom routing systems.

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