class VueRouter{...constructor(options){
    // Mount the vue instance
    this.app = null
    this.apps = []
    this.options = options
    // Route guard method
    this.beforeHooks = []
    this.resolveHooks = []
    this.afterHooks = []
    // Create a route match object
    this.matcher = createMatcher(options.routes || [],this)... }Copy the code

createMatcher

export function createMatcher(routes,router){
  const { pathList, pathMap, nameMap } = createRouteMap(routes)
  // Dynamically update the routing object
  function addRoutes(routes){
    createRouteMap(routes,pathList,pathMap,nameMap)
  }
  function addRoute(parentOrRoute,route){
    // Check whether the route or parent node exists
    const parent = ( typeofparentOrRoute ! = ='object')? nameMap[parentOrRoute] :undefined
    // Update the routing object based on the route to be added
    createRouteMap([route || parentOrRoute],pathList,pathMap,nameMap,parent)
    // Update the routing object to the parent route
    if(parent){
      createRouteMap(parent.alias.map(alias= > ({path:alias,children:[route]})),pathList,pathMap,nameMap,parent)
    }
  }
  // Get the routing object based on the routing path
  function getRoutes(){
    return pathList.map(path= > pathMap[path])
  }
  function match(raw,currentRoute,redirectedFrom){
    // Format the location of the jump
    const location = normalizeLocation(raw,currentRoute,false,router)
    const { name } = location
    if(name){
      const record = nameMap[name]
      if(process.env.NODE_ENV ! = ='production'){
        ...// The routing object must exist
      }
      // If the route object does not exist, create a route
      if(! record)return _createRoute(null,location)
      // Get route transport parameters
      const paramNames = record.regex.keys.filter(key= >! key.optional).map(key= > key.name)
      // Set parameters
      if(typeoflocation.params ! = ='object'){
        location.params = {}
      }
      // Transfer route parameters to jump parameters
      if(currentRoute && typeof currentRoute.params === 'object') {for(const key in currentRoute.params){
          if(! (keyin loaction.params) && paramNames.indexOf(key) > -1 ){
            location.params[key] = currentRoute.params[key]
          }
        }
      }
      location.path = fillParams(record.path,location.params,`name route "${name}"`)
      // Create a route
      return _createRoute(record,location,redirectedFrom)
    }else if(location.path){
      location.params = {}
      // Iterate over the route list
      for(let i = 0; i < pathList.length; i ++){
        const path = pathList[i]
        const record = pathMap[path]
        if(matchRoute(record.regex,location.path,location.params)){
          return _createRoute(record,location,redirectedFrom)
        }
      }
    }
    // Create a location route
    return _createRoute(null,location)
  }

  function redirect(record,location){
    const originalRedirect = record.redirect
    // Redirect the route
    let redirect = typeof originalRedirect === 'function' ? originalRedirect(createRoute(record,location,null,router)) : originalRedirect
    // Set the redirection route
    if(typeof redirect === 'string'){
      redirect = {path:redirect}
    }
    if(! redirect ||typeofredirect ! = ='object'){
      .../ / must redirect
      // Returns the current route object
      return _createRoute(null,location)
    }
    / / get the query/params hash
    const re = redirect
    const { name, path } = re
    let { query, params, hash} = location
    query = re.hasOwnProperty('query')? re.query : query params = re.hasOwnProperty('params')? re.params : params hash = re.hasOwnProperty('hash')? re.hash : hashif(name){
      const targetRecord = nameMap[name]
      ...// The routing object must
      // Returns the matched route object
      return match({_normalized:true,name,query,hash,params},undefined,location)
    }else if(path){
      // Parse the routing path
      const rawPath = resolveRecordPath(path,record)
      const resolvedPath = fillParams(rawPath,params,`redirect route with path "${rawPath}"`)
      return match({_normalized:true.path:resolvedPath,query,hash},undefined,location)
    }else{.../ / path/name must be
      // Create the current route object
      return _createRoute(null,location)
    }
  }

  function alias(record,location,matchAs){
    const aliasedPath = fillParams(matchAs,location.params,`aliased route with path "${matchAs}"`)
    // Returns the matching route object or the current route object
    const aliasedMatch = match({_normalized:true.path:aliasedPath})
    if(aliasedMatch){
      // Create a route object based on the matched route
      const matched = aliasedMatch.matched
      const aliasedRecord = matched[matched.length - 1]
      location.params = aliasedMath.params
      return _createRoute(aliasedRecord,location)
    }
    // Returns the current route object
    return _createRoute(null,location)
  }

  function _createRoute(record,location,redirectedFrom){
    // Redirect the route object or return the current route
    if(record && record.redirect){
      return redirect(record,redirectedFrom || location)
    }
    // Based on the matched route
    if(record && record.matchAs){
      return alias(record,location,record.matchAs)
    }
    // Create a routing object
    return createRoute(record,location,redirectedForm,router)
  }
  return {
    match,
    addRoute,
    getRoutes,
    addRoutes
  }
}
Copy the code

createRouteMap

export function createRouteMap(routes,oldPathList,oldPathMap,oldNameMap,parentRoute){
  // Initialize pathList/pathMap/nameMap
  const pathList = oldPathList || []
  const pathMap = oldPathMap || Object.create(null)
  const nameMap = oldNameMap || Object.create(null)
  // Generate a routing object/route based on the routing configuration
  routes.forEach(route= > {
    addRouteRecord(pathList,pathMap,nameMap,route,parentRoute)
  })
  // Ensure that * routes match at the end of the routing list
  for(let i = 0, l = pathList.length; i < l; i ++){
    if(pathList[i] === The '*'){
      pathList.push(pathList.slice(i,1) [0])
      l --
      i --
    }
  }
  if(process.env.NODE_ENV ! = ='production'){
    ...// The routing path does not start with/or *
  }
  return {
    pathList, // Routing path
    pathMap, // Store routing objects by path
    nameMap // Store route objects by route name}}Copy the code

addRouteRecord

function addRouteRecord(pathList,pathMap,nameMap,route,parent,matchAs){
  const { path, name } = route
  if(process.env.NODE_ENV ! = ='production'){
    ...// path required/Component cannot be a string. / Path cannot contain unescaped characters
  }
  // Compile the re option
  const pathToRegexpOptions = route.pathToRegexpOptions || {}
  // Formats the current node path
  const normalizedPath = normalizePath(path,parent,pathToRegexpOptions.strict)
  // Whether the route matching rule is case sensitive
  if(typeof route.caseSensitive === 'boolean'){
    pathToRegexpOptions.caseSensitive = route.caseSensitive
  }
  const record = {
    path: normalizedPath, // Format the routing path
    regex: compileRouteRegex(normalizedPath,pathToRegexpOptions), // Create a path-matching regular expression
    components:route.components || { default:route.component}, // Render component for the current path
    alias: route.alias ? typeof route.alias === 'string' ? [route.alias] : route.alias : [], // Routing links
    instances: {},enteredCbs: {},// Route guard
    name, // Route name
    parent, / / the parent node
    matchAs,
    redirect:route.redirect, / / redirection
    beforeEnter:router.beforeEnter, // Route guard
    meta:route.meta || {},
    props: route.props === null ? {} : route.components ? route.props : {default:route.props}
  }
  // The route has child nodes
  if(route.children){
    if(process.env.NODE_ENV ! = ='production'){
      ...// Child nodes must have redirect
    }
    // Extend the child node path to pathList/pathMap
    route.children.forEach(child= > {
      const childMatchAs = matchAs ? cleanPath(`${matchAs}/${child.path}`) : undefined
      addRouteRecord(pathList,pathMap,nameMap,child,record,childMathAs)
    })
  }
  // Store routing paths and routing objects
  if(! pathMap(record.path)){ pathList.push(record.path) pathMap[record.path] = record }if(route.alias ! = =undefined) {const aliases = Array.isArray(route.alias) ? route.alias : [route.alias]
    for(let i = 0; i < aliases.length; i ++){
      const alias = aliases[i]
      if(process.env.NODE_ENV ! = ='production' && alias === path){
        ... // Remove the route object alias == path
        continue
      }
      const aliasRoute = {path: alias, children: route.children }
      // Expand pathMap/pathList according to alias
      addRouteRecord(pathList,pathMap,nameMap,aliasRoute,parent,record.path || '/')}}if(name){
    if(! nameMap[name]){ nameMap[name] = record }else if(process.env.NODE_ENV ! = ='production' && !matchAs){
      ... // Generate the route object repeatedly}}}Copy the code

normalizePath

function normalizePath(path,parent,strict){
  // Is not a strict match, and is replaced with empty if the last string is a /
  if(! strict) path = path.replace(/ / / $/.' ')
  // Check that the path is normal
  if(path[0= = ='/') return path
  // Determine the root path
  if(parent == null) return path
  // Formats the path, replacing // with /
  return cleanPath(`${parent.path}/${path}`)}Copy the code

normalizeLocation

function normalizeLocation(raw,current,append,router){
  // Resolve the jump path
  let next = typeof raw === 'string' ? { path: raw } : raw
  // Determine if location is processed
  if(next._normalized){
    return next
  }else if(next.name){
    // There is a route name
    next = extend({},raw)
    const params = next.params
    if(params && typeof params === 'object'){
      next.params = extend({},params)
    }
    return next
  }
  // Failed to match the route name and path
  if(! next.path && next.params && current){ next = extend({},next) next._normalized =true
    const params = extend(extend({},current.params),next.params)
    // The current route name does not jump, and the jump parameters are merged
    if(current.name){
      next.name = current.name
      next.params = params
    }else if(current.matched.length){
      const rawPath = current.matched[current.matched.length - 1].path
      // Set the hop path to the last match of the current route
      next.path = fillParams(rawPath,params,`path ${current.path}`)}else if(process.env.NODE_ENV ! = ='production'){
      warn(false.`relative params navigation requires a current route`)}return next
  }
  // Resolve the jump path
  const parsedPath = parsePath(next.path || ' ')
  // Get the current path
  const basePath = (current && current.path) || '/'
  // Parse the path
  const path = parsedPath.path ? resolvePath(parsedPath.path,basePath,append||next.append) : basePath
  / / query
  const query = resolveQuery(parsedPath.query,next.query,router && router.options.parseQuery)
  // hash
  let hash = next.hash || parsedPath.hash
  if(hash && hash.charAt(0)! = =The '#'){
    hash = ` #${hash}`
  }
  return {
    _normalized:true,
    path,
    query,
    hash
  }

}
Copy the code