/* @flow *//*用于路径匹配的正则表达式对象。 */
import Regexp from 'path-to-regexp'
//用于清理 uri 上连续重复的 / 。
import {
cleanPath } from './util/path'
//断言,警告。
import {
assert, warn } from './util/warn'/*createRouteMap() 函数: 第一个参数 routes 就是 new VueRouter( { routes: [xxxx] } ) 中的 routes。 */
export function createRouteMap(routes: Array<RouteConfig>,oldPathList?: Array<string>,oldPathMap?: Dictionary<RouteRecord>,oldNameMap?: Dictionary<RouteRecord>,parentRoute?: RouteRecord
): {
pathList: Array<string>,pathMap: Dictionary<RouteRecord>,nameMap: Dictionary<RouteRecord>,
} {
// the path list is used to control path matching priority//存放所有路由的 path 。const pathList: Array<string> = oldPathList || []// $flow-disable-line//以 path 作为 key,存放所有的路由描述对象。const pathMap: Dictionary<RouteRecord> = oldPathMap || Object.create(null)// $flow-disable-line//以 name 作为 key,存放所有路由描述对象。const nameMap: Dictionary<RouteRecord> = oldNameMap || Object.create(null)//routes 是一个数组对象。也就是用户手写的 new VueRouter( { routes: [xxx] } ) 的 routes 配置数据//遍历routes数组的数据,将所有元素转化为 router record 对象。且会被记录到 pathMap, nameMap 对象中。routes.forEach((route) => {
//pathList: 全局的 pathList,所有路由创建过程中是同一个 pathList.//pathMap: 全局的 pathMap, 所有路由创建过程中是同一个 pathMap.//nameMap: 全局的 nameMap, 所有路由创建过程中是同一个 nameMap.//route: 就是要转化为的 router record 对象的数据。//parentRoute:表示父 route 数据转化成的 router record 对象。addRouteRecord(pathList, pathMap, nameMap, route, parentRoute)})/*** 这一段主要处理 pathList 中的 path == ‘*’ 的路径,且移到数组末尾。*///依次遍历 pathList。 pathList 中元素都是完整绝对路径。// 如果 pathList 中存在 path 路径为 “*”,则移除该元素,并且追加到 pathList 数组末尾。for (let i = 0, l = pathList.length; i < l; i++) {
if (pathList[i] === '*') {
//先移除第 i 个元素,pathList.splice() 返回被移除元素的数组。//因为只移除了一个,则获取下标为0的元素,然后塞到数组的末尾。pathList.push(pathList.splice(i, 1)[0])//之所以要 l--, 是因为 l ~ length 的元素就是 * 号,已经处理过了。//如果 l 不 --, 存在多个 * 的情况,则会死循环。l--//此时i+1 ~ length-1 的元素已经迁移。后一个元素已经占在了 i 位置。// 所以需要 i--,然后下一轮循环就能取到下一个没有判断*的元素。i--}}/*如果是开发环境,且 pathList 中 path 元素,不是以 * 或者 / 开头,则报警告。*/if (process.env.NODE_ENV === 'development') {
// warn if routes do not include leading slashesconst found = pathList// check for missing leading slash.filter((path) => path && path.charAt(0) !== '*' && path.charAt(0) !== '/')if (found.length > 0) {
const pathNames = found.map((path) => `- ${
path}`).join('\n')warn(false,`Non-nested routes must include a leading slash character. Fix the following routes: \n${
pathNames}`)}}/*返回 matcher 对象. {pathList, //记录所有 route 的完整的绝对路径 path。pathMap, //记录所有的 router record 对象,以 path 作为 key 进行存储。nameMap //记录所有的 router record 对象,以 name 作为 key 就行存储。}*/return {
pathList,pathMap,nameMap,}
}/*addRouteRecord() 函数,用于添加 route 数据。pathList: 存储路由 path。pathMap: 以 path 为 key, 存储路由描述信息。nameMap: 以 name 为 key, 存储路由描述信息。route: 用户配置的当个路由数据信息。 */
function addRouteRecord(pathList: Array<string>,pathMap: Dictionary<RouteRecord>,nameMap: Dictionary<RouteRecord>,route: RouteConfig,parent?: RouteRecord,matchAs?: string
) {
/*route 数据为:{name: xxx,path: xxx,component: xxx,children: [ {name: xxx,path: xxx,component: xxx,}]meta: xxx}*///获取配置信息中的 path,name 属性。const {
path, name } = route//非开发模式下if (process.env.NODE_ENV !== 'production') {
//如果 path 没有指定,则报警告提示。assert(path != null, `"path" is required in a route configuration.`)assert(//如果 route.component 不是字符串类型。 todotypeof route.component !== 'string',`route config "component" for path: ${
String(path || name)} cannot be a ` + `string id. Use an actual component instead.`)warn(// eslint-disable-next-line no-control-regex//判断path是不是只包含 128 个 ascii 码的字符。如果不是,则报警告。!/[^\u0000-\u007F]+/.test(path),`Route with path "${
path}" contains unencoded characters, make sure ` +`your path is correctly encoded before passing it to the router. Use ` +`encodeURI to encode static segments of your path.`)}/*pathToRegexpOptions 内容为:{sensitive 大小写敏感 (default: false)strict 末尾斜杠是否精确匹配 (default: false)end 全局匹配 (default: true)start 从开始位置展开匹配 (default: true)delimiter 指定其他分隔符 (default: '/')endsWith 指定标准的结束字符whitelist 指定分隔符列表 (default: undefined, any character)}*///pathToRegexpOptions 表示编译正则的选项。//可以通过配置 route 的 pathToRegexpOptions 参数添加高级配选项。默认是空对象。const pathToRegexpOptions: PathToRegexpOptions =route.pathToRegexpOptions || {
}//对路径名称进行归一化处理。//绝对路径直接返回;相对路径就拼接父 path。const normalizedPath = normalizePath(path, parent, pathToRegexpOptions.strict)//route.caseSensitive 属性如果存在,则设置到 pathToRegexpOptions 中。// caseSensitive: 表示大小写敏感。if (typeof route.caseSensitive === 'boolean') {
//大小写敏感的正则表达式配置项。pathToRegexpOptions.sensitive = route.caseSensitive}const record: RouteRecord = {
//normalizedPath: 完整的绝对路径。path: normalizedPath,//根据完整的路径,以及路径匹配配置参数,生成路径匹配正则对象。regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),//设置 component。//如果是别名路由的创建,则 component 为 undefined。components: route.components || {
default: route.component },//设置路由别名。别名类似于重定向,但是显示的路径会是别名的路径。//别名可以设置多个,用数组表示;如果只有一个且是字符串,则归一化为数组。alias: route.alias? typeof route.alias === 'string'? [route.alias]: route.alias: [],instances: {
},enteredCbs: {
},//路由名字name,//父路由 record 对象。parent,//如果是 root route, 则 matchAS 为 undefined.matchAs,//记录路由的 redirect 重定向属性。redirect: route.redirect,//记录路由独享的守卫/* {path: "/users/:id",component: xxx,beforeEnter: ( to, from ) => { return false } } */beforeEnter: route.beforeEnter,//记录 route 元数据。一般用于配置keepalive, required 等。meta: route.meta || {
},//props:// 如果没配置有 route.props,则默认为空对象。// 如果配置有 route.props, 则如果 component 存在,则记录 route.props 数据。// 说明: props 类似于 query, params,都是用于携带路由传参的。不过 props 会自动把数据传递到组件的 props 中。route.props == null? {
}: route.components? route.props: {
default: route.props },}//如果存在子路由数据。if (route.children) {
// Warn if route is named, does not redirect and has a default child route.// If users navigate to this route by name, the default child will// not be rendered (GH Issue #629)if (process.env.NODE_ENV !== 'production') {
if (//如果路由 A 有名称,且没有配置重定向属性 redirect。如果子路由 B 配置的路径为 “/”,// 则此时如果使用命名路由的方式进行跳转到 A 路由,此时子路由 B 不会被渲染。route.name &&!route.redirect &&route.children.some((child) => /^\/?$/.test(child.path))) {
warn(false,`Named Route '${
route.name}' has a default child route. ` +`When navigating to this named route (:to="{name: '${
route.name}'"), ` +`the default child route will not be rendered. Remove the name from ` +`this route and use the name of the default child route for named ` +`links instead.`)}}//先序遍历的形式依次将子 route 的数据以此生成 router record对象。route.children.forEach((child) => {
//如果 route 是用户真实配置的 route 数据,则 matchAs 为 undefine。//如果 route 是 alias 生成的 route 数据,则 matchAs 为被别名的完整路径。// 子 route 会通过 matchAs 记录没有被别名的完整路径。const childMatchAs = matchAs? cleanPath(`${
matchAs}/${
child.path}`): undefined//pathList: 全局的 pathList,所有路由创建过程中是同一个 pathList.//pathMap: 全局的 pathMap, 所有路由创建过程中是同一个 pathMap.//nameMap: 全局的 nameMap, 所有路由创建过程中是同一个 nameMap.//child: 就是要转化的 route 数据。//record: 就是 parent。addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs)})}//如果 record.path 没有被记录到 pathMap 中。//后面出现相同的 record.path,相当于直接丢弃。if (!pathMap[record.path]) {
//将 record.path 收集到 pathList 中。 此时的 path 是完整的绝对路径。pathList.push(record.path)//将 router record 对象 以 record.path 作为 key,记录到 pathMap 中。pathMap[record.path] = record}/*如果配置了 route.alias 元素。(1) 将 router.alias 归一化为数组。(2) 过滤掉与 route.path 同名的 route.alias中的元素。(3) 将 route.alias 中元素, 封装成一个 route 数据。即 { path: router.alias[index], children },然后也被遍历生成 router record, 以及子 router record。*///如果配置了 route.alias 属性。if (route.alias !== undefined) {
//如果 route.alias 不是对象(则是字符串),则归一化为数组。const aliases = Array.isArray(route.alias) ? route.alias : [route.alias]//判断别名数组 aliases 存在与 router.path 相同,则打印警告信息。且将这条 alias 忽略。for (let i = 0; i < aliases.length; ++i) {
const alias = aliases[i]if (process.env.NODE_ENV !== 'production' && alias === path) {
warn(false,`Found an alias with the same value as the path: "${
path}". You have to remove that alias. It will be ignored in development.`)// skip in dev to make it workcontinue}//以别名作为 path,生成对应的 route record 对象。const aliasRoute = {
path: alias,children: route.children,}addRouteRecord(//全局的 pathList,所有路由创建过程中是同一个 pathList.pathList,//全局的 pathMap, 所有路由创建过程中是同一个 pathMap.pathMap,//全局的 nameMap, 所有路由创建过程中是同一个 nameMap.nameMap,//将别名封装成一个 route 数据。 { path: alias, children: [xxxx] }aliasRoute,//parent 是一个将 route 数据转换为 route record 对象。parent,//对于别名的 route record,matchAs 就是 record.path;//record.path 就是被别名的 path 的完整路径。record.path || '/' // matchAs)}}/*** 1、主要是判断 route.name 是否存在,如果存在,则作为 key,value 为 router record, 记录到 nameMap 中。* 2、如果 name 已经被记录到 nameMap 中,则直接忽略当前 route 数据。* 3、如果 name 还没有被记录到 nameMap 中,则进行记录。*///如果 route.name 存在if (name) {
//如果不存在重复的 name,则在 nameMap 中以 name 作为 key,存储 router record。if (!nameMap[name]) {
nameMap[name] = record//如果是重复的 name,且不是生产环境,且 matchAs 不为空,则报警告信息。} else if (process.env.NODE_ENV !== 'production' && !matchAs) {
warn(false,`Duplicate named routes definition: ` +`{ name: "${
name}", path: "${
record.path}" }`)}}
}/*编译路由正则表达式。 */
function compileRouteRegex(//此时的 path 是完整的绝对路径。path: string,//正则匹配高级配置选项。pathToRegexpOptions: PathToRegexpOptions
): RouteRegExp {
//Regexp 是 path-to-regexp 对象。 vue-router 使用 path-to-regexp.js 来进行路由规则匹配。//生成指定 path 的路由匹配正则对象。const regex = Regexp(path, [], pathToRegexpOptions)//单纯的判断是否有重复的 key.name 存在,如果有,则在非生产环境下报告警告信息。//没有实质性处理。if (process.env.NODE_ENV !== 'production') {
//创建一个 keys 对象。const keys: any = Object.create(null)//如果 key.name 已经存在于 keys 中,则非生产环境会报警告信息。//这里仅仅是判断是否有重复的,有就给个警告提示,不做其他。regex.keys.forEach((key) => {
warn(!keys[key.name],`Duplicate param keys in route with path: "${
path}"`)keys[key.name] = true})}//返回正则匹配对象。 作为 router record 对象的 regex 属性。return regex
}/*对路径进行归一化处理。1、route: 数据中配置的 path 路径。2、parent: 表示是父 route。 { path: "parentPath", children: [{ path: "path" }] }3、strict: 末尾斜杠是否精确匹配 (default: false) */
function normalizePath(path: string,parent?: RouteRecord,strict?: boolean
): string {
//如果 strict 为 false,则去除 path 末尾的 /。if (!strict) path = path.replace(/\/$/, '')//path[0] 表示获取第一个字符的子串。//如果 path 是以 "/" 开头,则表示是绝对路径,不需要拼接父路径。if (path[0] === '/') return path//如果 parent 不存在,则没有父路径可以拼接。if (parent == null) return path//用父 route 的 path 进行拼接。//cleanPath 用于处理多个 “/” 连续的情形。 比如 /user/name//age => /user/name/agereturn cleanPath(`${
parent.path}/${
path}`)
}
详细解决方案
vue-router3源码注解系列 /src/create-route-map.js
热度:40 发布时间:2023-10-26 11:38:35.0
相关解决方案
- !使用JDNI时,报 Cannot create JDBC driver of class '' for connect URL 'null'
- 关于异常:attempt to create delete event with null entity
- Cannot create a session after the response has been committed,该怎么解决
- java.lang.OutOfMemoryError: unable to create new native thread内存泄漏分析-备忘解决办法
- Cannot create a session after the response has been committed解决办法
- 调用WS接口,客户端抛出错误$Proxy37.create(Unknown Source)
- Cannot create PoolableConnectionFactory (尝试连线已失败。)解决思路
- WebRequest.Create()的参数有关问题
- (急)在数据库 'master' 中回绝了 CREATE DATABASE 权限。~~~~~~~~~~
- System.Net.WebRequest.Create(Url);防火墙要开设哪些端口?解决思路
- 在数据库 'master' 中拒绝了 CREATE DATABASE 权限。解决方案
- CREATE TABLE EMP(职员信息表)在SQL中如何排序
- create view WITH CHECK OPTION的有关问题
- oracle 11.2.0 $emca -config dbcontrol db -repos create; 报错解决方法
- 异常:"Failed to create netty connection"
- 怎么解决 Failed to create a new SAX parser
- sqlite create table時是否能設定資料型態或長度呢,该怎么解决
- create database的for load选项有关问题
- 使用MicroStrategy 打开desktop时 activex component cant create object 异常 应该怎么解决
- 有关sql中 create 语句 的 foreign key 子句解决方法
- Create Index出現的奇怪問題。希望高手幫忙解答。该如何处理
- DB2 - create table schema_name.table_name like schema_name1.table_name1,该如何处理
- tomcat启动时cannot create poolableConnectionFactory解决方案
- RIP协议中table、route、neighbor、distance_table几张表的差异
- 制作帮助文件时提示:HHC6000: Error: An error occurred attempting to create,该如何解决
- 输入svnadmin create C:\svndemo\TEXT,系统提醒找不到指定的路径啊5555555,哥哥
- vsftp “553 Could not create file.” 异常
- ror支配后找不到 js css 图片等文件-No route matches [GET] "/assets
- 新手,socket Unable to create socket
- thinkphp $model_user->create($data)数据格式不对,该如何解决