fillParams
const regexCompileCache = Object.create(null)
function fillParams(path,params,routeMsg){
params = params || {}
try{
const filler = regexCompileCache[path] || (regexCompileCache[path] = Regexp.compile(path))
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]
}
}
path
resolvePath
function resolvePath(relative,base,append){
const firstChar = relative.charAt(0)
if(firstChar=== '/'){
return relative
}
if(firstChar === '?' || firstChar === '#'){
return base + relative
}
const stack = base.split('/')
if(!append || !stack[stack.length - 1]){
stack.pop()
}
const segments = relative.replace(/^\//,'').split('/')
for(let i = 0; i < segments.length; i ++){
const segment = segments[i]
if(segment === '..'){
stack.pop()
}else if(segment !== '.'){
stack.push(segment)
}
}
if(stack[0] !== ''){
stack.unshift('')
}
return stack.join('/')
}
parsePath
function parsePath(path){
let hash = ''
let query = ''
const hashIndex = path.indexOf('#')
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
}
}
cleanPath
function cleanPath(path){
return path.replace(/\/\//g,'/')
}
query
parseQuery
function parseQuery(query){
const res = {}
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
}
resolveQuery
function resolveQuery(query,extraQuery,_parseQuery){
const parse = _parseQuery || parseQuery
let parsedQuery
try{
parsedQuery = parse(query || '')
}catch(e){
process.env.NODE_ENV !== 'production' && warn(false,e.message)
parsedQuery = {}
}
for(const key in extraQuery){
const value = extraQuery[key]
parsedQuery = Array.isArry(value) ? value.map(castQueryParamValue) : castQueryParamValue
}
return parsedQuery
}
const castQueryParamValue = value => (value== null || typeof value === 'object' ? value : String(value) )
stringifyQuery
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
}
resolve-components
flatten
const flatten = (arr) => Array.prototype.concat.apply([],arr)
flatMapComponents
const flatMapComponents = (matched,fn) => flatten(matched.map(m => Object.keys(m.components).map(key => fn(m.components[key],m.instances[key],m,key))))
isESModule
const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol'
const isESModule = (obj) => obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module')
resolveAsyncComponents
function resolveAsyncComponents(matched){
return (to,from,next) => {
let hasASync = false
let pending = 0
let error = null
flatMapComponents(matched,(def,_,match,key) => {
if(typeof def === 'function' && def.cid === undefined){
hasAsync = true
pending ++
const resolve = once(resolvedDef => {
if(isESModule(resolvedDef)){
resolvedDef = resolvedDef.default
}
def.resolved = typeof resolvedDef === 'function' ? resolvedDef : _Vue.extend(resolvedDef)
match.components[key] = resolvedDef
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
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()
}
}
route
const trailingSlashRE = /\/?$/
createRoute
function createRoute(record,location,redirectedFrom,router){
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)
}
clone
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
}
}
START
const START = createRoute(null,{path:'/'})
formatMatch
function formatMatch(record){
const res = []
while(record){
res.unshift(record)
record = record.parent
}
return res
}
getFullPath
function getFullPath({path,query = {},hash = ''},_stringifyQuery){
const stringify = _stringifyQuery || stringifyQuery
return (path || '/') + stringify(query) + hash
}
isSameRoute
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
}
}
isObjectEqual
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)
})
}
isIncludedRoute
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(!(key in current)){
return false
}
}
return true
}
handleRouteEntered
function handleRouteEntered(route){
for(let i = 0; i < route.matched.length; i ++){
const record = route.matched[i]
for(const name in record.instances){
const instance = record.instanced[name]
const cbs = record.enterCbs[name]
if(!instance || !cbs) continue
delete record.enterCbs[name]
for(let i = 0; i < cbs.length; i++){
if(!instance._isBeingDestroyed) cbs[i](instance)
}
}
}
}