fillParams

const regexCompileCache = Object.create(null)
function fillParams(path,params,routeMsg){
  params = params || {}
  try{
    // Create a compiled object based on the path
    const filler = regexCompileCache[path] || (regexCompileCache[path] = Regexp.compile(path))
    // If a string is matched, the path match is compiled in the first place
    if(typeof params.pathMatch === 'string') params[0] = params.pathMatch
    return filler(params,{pretty:true})}catch(e){
    if(process.env.NODE_ENV ! = ='production'){
      warn(typeof params.pathMatch==='string'.`missing param for ${routeMsg}:${e.message}`)}return ' '
  }finally{
    delete params[0]}}Copy the code

path

resolvePath

function resolvePath(relative,base,append){
  // Query the first character of the path
  const firstChar = relative.charAt(0)
  // Returns directly starting with /
  if(firstChar=== '/') {return relative
  }
  // Start with search or hash and add base returns
  if(firstChar === '? ' || firstChar === The '#') {return base + relative
  }
  // Store path at each level
  const stack = base.split('/')
  // If no additional path is required or the last base path is empty, the last base path is removed
  if(! append || ! stack[stack.length -1]){
    stack.pop()
  }
  // Remove the head/cut
  const segments = relative.replace(/ ^ / / /.' ').split('/')
  for(let i = 0; i < segments.length; i ++){
    const segment = segments[i]
    / / /.. Indicates to jump to a directory
    if(segment === '.. '){
      stack.pop()
    }else if(segment ! = ='. ') {// If no, the storage path
      stack.push(segment)
    }
  }
  // Always start with a /
  if(stack[0]! = =' '){
    stack.unshift(' ')}return stack.join('/')}Copy the code

parsePath

// Cut path => query/hash/path
function parsePath(path){
  let hash = ' '
  let query = ' '
  const hashIndex = path.indexOf(The '#')
  if(hashIndex >= 0){
    hash = path.slice(hashIndex)
    path = path.slice(0,hashIndex)
  }
  const queryIndex = path.indexOf('? ')
  if(queryIndex >= 0){
    query = path.slice(queryIndex + 1)
    path = path.slice(0,queryIndex)
  }
  return {
    path,
    query,
    hash
  }
}
Copy the code

cleanPath

// Replace // => /
function cleanPath(path){
  return path.replace(/\/\//g.'/')}Copy the code

query

parseQuery

// Parse the kv of query
function parseQuery(query){
  const res = {}
  // Remove the header? / # / &
  query = query.trim().replace(/ ^ (\? # | | &) /.' ')
  if(! query){return res
  }
  query.split('&').forEach(param= > {
    const parts = param.replace(/\+/g.' ').split('=')
    const key = decode(parts.shift())
    const val = parts.length > 0 ? decode(parts.join('=')) :null
    if(res[key] === undefied){
      res[key] = val
    }else if (Array.isArray(res[key])){
      res[key].push(val)
    }else{
      res[key] = [res[key],val]
    }
  })
  return res
}
Copy the code

resolveQuery

function resolveQuery(query,extraQuery,_parseQuery){
  // Use custom or default query parsing
  const parse = _parseQuery || parseQuery
  let parsedQuery
  / / query
  try{
    parsedQuery = parse(query || ' ')}catch(e){ process.env.NODE_ENV ! = ='production' && warn(false,e.message)
    parsedQuery = {}
  }
  // Extend to query
  for(const key in extraQuery){
    const value = extraQuery[key]
    parsedQuery = Array.isArry(value) ? value.map(castQueryParamValue) : castQueryParamValue
  }
  return parsedQuery
}
// Returns either null or String objects
const castQueryParamValue = value= > (value== null || typeof value === 'object' ? value : String(value) )
Copy the code

stringifyQuery

// Merge query kv into a string
function stringifyQuery(obj){
  const res = obj ? Object.keys(obj).map(key= > {
    const val = obj[key]
    if(val === undefined) {return ' '
    }
    if(val === null) {return encode(key)
    }
    if(Array.isArray(val)){
      const result = []
      val.forEach(val2= > {
        if(val2 === undefined) {return
        }
        if(val2 === null){
          result.push(encode(key))
        }else{
          result.push(encode(key) + '=' + encode(val2))
        }
      })
      return result.join('&')}return encode(key) + '=' + encode(val)
  }).filter(x= > x.length > 0).join('&') : null
}
Copy the code

resolve-components

flatten

const flatten = (arr) = > Array.prototype.concat.apply([],arr)
Copy the code

flatMapComponents

const flatMapComponents = (matched,fn) = > flatten(matched.map(m= > Object.keys(m.components).map(key= > fn(m.components[key],m.instances[key],m,key))))
Copy the code

isESModule

// Determine the ES6 Module
const hasSymbol = typeof Symbol= = ='function' && typeof Symbol.toStringTag === 'symbol'
const isESModule = (obj) = > obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module')
Copy the code

resolveAsyncComponents

function resolveAsyncComponents(matched){
  return (to,from,next) = > {
    let hasASync = false
    let pending = 0
    let error = null
    // def component/instance /match Route object /key Component key
    flatMapComponents(matched,(def,_,match,key) = > {
      // If it is a function component and CID is not added, it is considered asynchronous
      if(typeof def === 'function' && def.cid === undefined){
        hasAsync = true
        pending ++
        const resolve = once(resolvedDef= > {
          // For ES6 modules, obtain components through default
          if(isESModule(resolvedDef)){
            resolvedDef = resolvedDef.default
          }
          // Parse the component
          def.resolved = typeof resolvedDef === 'function' ? resolvedDef : _Vue.extend(resolvedDef)
          match.components[key] = resolvedDef
          // If component parsing is complete, go to the next step
          pending --
          if(pending <= 0){
            next()
          }
        })
        const reject = once(reason= > {
          const msg = `Failed to resolve async component ${key}:${reason}`process.env.NODE_ENV ! = ='production' && warn(false,msg)
          if(! error){ error = isError(reason) ? reason :new Error(msg)
            next(error)
          }
        })
        let res
        // Parse the component
        try{
          res = def(resolve,reject)
        }catch(e){
          reject(e)
        }
        if(typeof res.then === 'function'){
          res.then(resolve,reject)
        }else{
          const comp = res.component
          if(comp && typeof comp.then === 'function'){
            comp.then(resolve,reject)
          }
        }
      }
    })
    if(! hasAsync) next() } }Copy the code

route

// match /? At the end
const trailingSlashRE = / / /? $/
Copy the code

createRoute

function createRoute(record,location,redirectedFrom,router){
  // Create a route object
  const stringifyQuery = router && router.options.stringifyQuery
  let query = location.query || {}
  try{
    query = clone(query)
  }catch(e){}
  const route = {
    name: location.name || (record && record.name),
    meta: (record && record.meta) || {},
    path: location.path || '/'.hash: location.hash || ' ',
    query,
    params: location.params || {},
    fullPath: getFullPath(location,stringifyQuery),
    matched: record ? formatMatch(record) : {}
  }
  if(redirectedFrom){
    route.redirectedFrom = getFullPath(redirectedFrom,stringifyQuery)
  }
  return Object.freeze(route)
}
Copy the code

clone

/ / copy
function clone(value){
  if(Array.isArray(value)){
    return value.map(clone)
  }else if(value && typeof value === 'object') {const res = {}
    for(const key in value){
      res[key] = clone(value[key])
    }
    return res
  }else{
    return value
  }
}
Copy the code

START

const START = createRoute(null, {path:'/'})
Copy the code

formatMatch

// Recursively find the parent of the current route as a match object
function formatMatch(record){
  const res = []
  while(record){
    res.unshift(record)
    record = record.parent
  }
  return res
}
Copy the code

getFullPath

// Restore the current path based on the configured or default Query resolution
function getFullPath({path,query = {},hash = ' '},_stringifyQuery){
  const stringify = _stringifyQuery || stringifyQuery
  return (path || '/') + stringify(query) + hash
}
Copy the code

isSameRoute

// Determine whether two routing objects are equal
function isSameRoute(a,b,onlyPath){
  if(b === START){
    return a === b
  }else if(! b){return false
  }else if(a.path && b.path){
    return a.path.replace(trailingSlashRE,' ') === b.path.replace(trailingSlashRE,' ') && (onlyPath || (a.hash === b.hash && isObjectEqual(a.query,b.equery)))
  }else if(a.name && b.name){
    return a.name === b.name && (onlyPath || (a.hash === b.hash && isObjectEqual(a.query,b.equery) && isObjectEqual(a.params,b.params)))
  }else{
    return false}}Copy the code

isObjectEqual

// Check whether two objects are equal
function isObjectEqual(a={},b={}){
  if(! a || ! b)return a === b
  const aKeys = Object.keys(a).sort()
  const bKeys = Object.keys(b).sort()
  if(aKeys.length ! == bKeys.length){return false
  }
  return aKeys.every((key,i) = > {
    const aVal = a[key]
    const bKey = bKeys[i]
    if(bKey ! == key)return false
    const bVal = b[bKey]
    if(aVal === null || bVal === null) return aVal === bVal
    if(tyoeof aVal === 'object' && typeof bVal === 'object') {return isObjectEqual(aVal,bVal)
    }
    return String(aVal) === String(bVal)
  })
}
Copy the code

isIncludedRoute

// Check whether the route is included
function isIncludedRoute(current,target){
  return (current.path.replace(trailingSlashRE,' ').indexOf(target.path.replace(trailingSlashRE,' '= = =))0&& (! target.hash || current.hash === target.hash) && queryIncludes(current.query,target.query)) }function queryIncludes(current,target){
  for(const key in target){
    if(! (keyin current)){
      return false}}return true
}
Copy the code

handleRouteEntered

function handleRouteEntered(route){
  // Get the parent of the current route
  for(let i = 0; i < route.matched.length; i ++){
    const record = route.matched[i]
    for(const name in record.instances){
      // Get the vue instance and enterCbs callback that matches the route
      const instance = record.instanced[name]
      const cbs = record.enterCbs[name]
      if(! instance || ! cbs)continue
      // By default, the vue instance callback is cleared
      delete record.enterCbs[name]
      // Perform the Enter callback accordingly
      for(let i = 0; i < cbs.length; i++){
        if(! instance._isBeingDestroyed) cbs[i](instance) } } } }Copy the code