@wy
2020-04-17T19:06:06.000000Z
字数 3335
阅读 514
从名字可以看出来,用来做路径规范化。需要规范化的是,path、query、hash 这些值,并把 params 值融入到路径中。
在写路由配置的 routes 时,写了路径和组件的映射关系。
看下 normalizeLocation 接收的参数和返回值:
export function normalizeLocation (
raw: RawLocation,
current: ?Route,
append: ?boolean,
router: ?VueRouter
){
// 其他代码暂时省略
return {
_normalized: true,
path,
query,
hash
}
}
RawLocation 为类型
参数 raw,可以是一个字符串,也可以是对象,如果是对象,则一般是写在。
深入代码中来看:
// 传入字符串,则转成对象带 path 形式,
let next: Location = typeof raw === 'string' ? { path: raw } : raw
// 如果已经给格式化过了,直接返回
if (next._normalized) {
return next
} else if (next.name) { 如果有name,不需要格式化,复制后返回。
next = extend({}, raw)
const params = next.params
if (params && typeof params === 'object') {
next.params = extend({}, params)
}
return next
}
// 路径为空,并有当前路由信息对象,并且有 params,实际上就是把 params 融入到路径中。
// 例如有一个配置为: {path: '/user/?id', component: User} 根据路径后面的id不同渲染出不同的人物信息,都对应同一个组件
// 在组件中不跳转并改变id ,this.$roter.push({path:'',params:{id: '1'}}),这种方式的话,就符合下面的处理逻辑
if (!next.path && next.params && current) {
next = extend({}, next) // 复制一份,不改变原对象
next._normalized = true // 添加属性,标识已格式化
const params: any = extend(extend({}, current.params), next.params) // 合并改变的params到当前的params中
if (current.name) { // 当前路由信息只有name,则不需要把params融入到path中
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}`); // 填充params到路径中
} else if (process.env.NODE_ENV !== 'production') {
warn(false, `relative params navigation requires a current route.`)
}
return next // 返回已格式化好的数据
}
// 如果有存在 path,说明有路径,则需要解析路径,因为路径中可能存在各种形式,例如 /path?a=1 /path?a=1#b=1&c=2
// 先解析路径
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}`
}
此函数的作用是,填充路径参数,得到完成的路径。
例如,需要填充的数据:
let params = {
id: 1,
name: 'leo'
}
路径:
const path = '/user/:id/:name';
得到的完成路径为:
'/user/1/leo'
这里使用到了第三方模块,path-to-regexp,要自己测试的话,注意安装的版本为:1.7.0,和 vue-router 源码中安装的模块一致,否则测试时会踩坑。
地址:https://github.com/pillarjs/path-to-regexp,可查看详细用法,这里对用到的方法做简短的使用:
import Regexp from 'path-to-regexp'
const path = '/user/:id/:name';
const filter = Regexp.compile(path);
let filter = r.compile(path)
const fillPath = filter({name:'leo', id: 1});
console.log(fillPath)
打印的结果为:
"/user/1/leo"
作用从代码中能看出来,就是把数据填充到路径中,返回完成的路径。
明白了 path-to-regexp 的用处,再看代码变得异常简单明了
/* @flow */
import { warn } from './warn'
import Regexp from 'path-to-regexp'
// 做缓存用,对同一个路径不同求函数两次
const regexpCompileCache: {
[key: string]: Function
} = Object.create(null)
export function fillParams (
path: string,
params: ?Object,
routeMsg: string
): string {
params = params || {}
try {
// 得到一个填充的函数,并缓存
const filler =
regexpCompileCache[path] ||
(regexpCompileCache[path] = Regexp.compile(path))
// Fix #2505 resolving asterisk routes { name: 'not-found', params: { pathMatch: '/not-found' }}
// and fix #3106 so that you can work with location descriptor object having params.pathMatch equal to empty string
// 这里解决的问题,当路径为 path = '/user/*' 时,需要不断改变 * 处的值,则可以填充 pathMatch 对应的值
// 例如改变路由时:{path: '/user/*', params: {pathMatch: 1}},怎会得到的完成路由时 '/user/1'
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 the 0 if it was added
delete params[0]
}
}