class VueRouter{
...
constructor(options){
this.app = null
this.apps = []
this.options = options
this.beforeHooks = []
this.resolveHooks = []
this.afterHooks = []
this.matcher = createMatcher(options.routes || [],this)
...
}
createMatcher
export function createMatcher(routes,router){
const { pathList, pathMap, nameMap } = createRouteMap(routes)
function addRoutes(routes){
createRouteMap(routes,pathList,pathMap,nameMap)
}
function addRoute(parentOrRoute,route){
const parent = ( typeof parentOrRoute !== 'object' ) ? nameMap[parentOrRoute] : undefined
createRouteMap([route || parentOrRoute],pathList,pathMap,nameMap,parent)
if(parent){
createRouteMap(parent.alias.map(alias => ({path:alias,children:[route]})),pathList,pathMap,nameMap,parent)
}
}
function getRoutes(){
return pathList.map(path => pathMap[path])
}
function match(raw,currentRoute,redirectedFrom){
const location = normalizeLocation(raw,currentRoute,false,router)
const { name } = location
if(name){
const record = nameMap[name]
if(process.env.NODE_ENV !== 'production'){
...
}
if(!record) return _createRoute(null,location)
const paramNames = record.regex.keys.filter(key => !key.optional).map(key => key.name)
if(typeof location.params !== 'object'){
location.params = {}
}
if(currentRoute && typeof currentRoute.params === 'object'){
for(const key in currentRoute.params){
if(!(key in loaction.params) && paramNames.indexOf(key) > -1 ){
location.params[key] = currentRoute.params[key]
}
}
}
location.path = fillParams(record.path,location.params,`name route "${name}"`)
return _createRoute(record,location,redirectedFrom)
}else if(location.path){
location.params = {}
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)
}
}
}
return _createRoute(null,location)
}
function redirect(record,location){
const originalRedirect = record.redirect
let redirect = typeof originalRedirect === 'function' ? originalRedirect(createRoute(record,location,null,router)) : originalRedirect
if(typeof redirect === 'string'){
redirect = {path:redirect}
}
if(!redirect || typeof redirect !== 'object'){
...
return _createRoute(null,location)
}
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 : hash
if(name){
const targetRecord = nameMap[name]
...
return match({_normalized:true,name,query,hash,params},undefined,location)
}else if(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{
...
return _createRoute(null,location)
}
}
function alias(record,location,matchAs){
const aliasedPath = fillParams(matchAs,location.params,`aliased route with path "${matchAs}"`)
const aliasedMatch = match({_normalized:true,path:aliasedPath})
if(aliasedMatch){
const matched = aliasedMatch.matched
const aliasedRecord = matched[matched.length - 1]
location.params = aliasedMath.params
return _createRoute(aliasedRecord,location)
}
return _createRoute(null,location)
}
function _createRoute(record,location,redirectedFrom){
if(record && record.redirect){
return redirect(record,redirectedFrom || location)
}
if(record && record.matchAs){
return alias(record,location,record.matchAs)
}
return createRoute(record,location,redirectedForm,router)
}
return {
match,
addRoute,
getRoutes,
addRoutes
}
}
createRouteMap
export function createRouteMap(routes,oldPathList,oldPathMap,oldNameMap,parentRoute){
const pathList = oldPathList || []
const pathMap = oldPathMap || Object.create(null)
const nameMap = oldNameMap || Object.create(null)
routes.forEach(route => {
addRouteRecord(pathList,pathMap,nameMap,route,parentRoute)
})
for(let i = 0, l = pathList.length; i < l; i ++){
if(pathList[i] === '*'){
pathList.push(pathList.slice(i,1)[0])
l --
i --
}
}
if(process.env.NODE_ENV !== 'production'){
...
}
return {
pathList,
pathMap,
nameMap
}
}
addRouteRecord
function addRouteRecord(pathList,pathMap,nameMap,route,parent,matchAs){
const { path, name } = route
if(process.env.NODE_ENV !== 'production'){
...
}
const pathToRegexpOptions = route.pathToRegexpOptions || {}
const normalizedPath = normalizePath(path,parent,pathToRegexpOptions.strict)
if(typeof route.caseSensitive === 'boolean'){
pathToRegexpOptions.caseSensitive = route.caseSensitive
}
const record = {
path: normalizedPath,
regex: compileRouteRegex(normalizedPath,pathToRegexpOptions),
components:route.components || { default:route.component},
alias: route.alias ? typeof route.alias === 'string' ? [route.alias] : route.alias : [],
instances:{},
enteredCbs:{},
name,
parent,
matchAs,
redirect:route.redirect,
beforeEnter:router.beforeEnter,
meta:route.meta || {},
props: route.props === null ? {} : route.components ? route.props : {default:route.props}
}
if(route.children){
if(process.env.NODE_ENV !== 'production'){
...
}
route.children.forEach(child => {
const childMatchAs = matchAs ? cleanPath(`${matchAs}/${child.path}`) : undefined
addRouteRecord(pathList,pathMap,nameMap,child,record,childMathAs)
})
}
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){
...
continue
}
const aliasRoute = {path: alias, children: route.children }
addRouteRecord(pathList,pathMap,nameMap,aliasRoute,parent,record.path || '/')
}
}
if(name){
if(!nameMap[name]){
nameMap[name] = record
}else if(process.env.NODE_ENV !== 'production' && !matchAs){
...
}
}
}
normalizePath
function normalizePath(path,parent,strict){
if(!strict) path = path.replace(/\/$/,'')
if(path[0] === '/') return path
if(parent == null) return path
return cleanPath(`${parent.path}/${path}`)
}
normalizeLocation
function normalizeLocation(raw,current,append,router){
let next = typeof raw === 'string' ? { path: raw } : raw
if(next._normalized){
return next
}else if(next.name){
next = extend({},raw)
const params = next.params
if(params && typeof params === 'object'){
next.params = extend({},params)
}
return next
}
if(!next.path && next.params && current){
next = extend({},next)
next._normalized = true
const params = extend(extend({},current.params),next.params)
if(current.name){
next.name = current.name
next.params = params
}else if(current.matched.length){
const rawPath = current.matched[current.matched.length - 1].path
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
}
const parsedPath = parsePath(next.path || '')
const basePath = (current && current.path) || '/'
const path = parsedPath.path ? resolvePath(parsedPath.path,basePath,append||next.append) : basePath
const query = resolveQuery(parsedPath.query,next.query,router && router.options.parseQuery)
let hash = next.hash || parsedPath.hash
if(hash && hash.charAt(0) !== '#'){
hash = `#${hash}`
}
return {
_normalized:true,
path,
query,
hash
}
}