@wy
2020-04-17T11:06:06.000000Z
字数 3335
阅读 626
从名字可以看出来,用来做路径规范化。需要规范化的是,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.paramsif (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.namenext.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): basePathconst query = resolveQuery(parsedPath.query,next.query,router && router.options.parseQuery)let hash = next.hash || parsedPath.hashif (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 addeddelete params[0]}}