Vue-Router源码分析(二): 路由映射表创建

js 复制代码
const router = new VueRouter({
  routes: [
    {
      path: '/',
      component: Home
    },
    {
      path: '/user',
      component: User,
      children: [
        {
          path: 'profile',
          component: UserProfile
        },
        {
          path: 'posts',
          component: UserPosts
        },
        {
          path: 'about',
          component: About
        }
      ]
    }
  ]
})

在我们使用Vue-Router配置路由信息时,这段代码是最常见的,routes对象接受一个对象数组,其中每个对象代表着一个路由信息,最核心就是path、component属性: path字段表示location的路由信息,component表示改路由对应的组件,当然还有一些其他属性,name、meta等等。

根据以上配置,Vue-Router在初始化时做了以下操作,保证path 一定能对应指定的component组件。

树的扁平化

在源码中:src/create-route-map.js文件中,将用户传递进来的routes属性遍历执行addRouteRecord方法

js 复制代码
routes.forEach(route => {
   addRouteRecord(pathList, pathMap, nameMap, route, parentRoute)
})

该方法中声明了一个RouteRecord对象,用来存储遍历的路由信息:

js 复制代码
  const record: RouteRecord = {
    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, //路由name
    parent, // 路由对应的父组件,这个属性很重要, 在递归是找到父组件的依据
    matchAs,
    redirect: route.redirect, // 重定向
    beforeEnter: route.beforeEnter, //钩子函数
    meta: route.meta || {}, //元信息
    props:
      route.props == null
        ? {}
        : route.components
          ? route.props
          : { default: route.props } //组件props
  }

接着判断route是否有children配置属性,执行递归操作。

js 复制代码
  if (route.children) {

   ......

    route.children.forEach(child => {

      ......

      // 递归执行,
      addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs)
    })
  }

这里有个点需要注意,

  • 在每次递归是都会去判断当前路由信息是否有parent,如果存在就要去拼接parent路由path和当前子路由path路劲,具体在normalizePath方法中:
js 复制代码
function normalizePath (
  path: string,
  parent?: RouteRecord,
  strict?: boolean
): string {
  // 正则相关
  if (!strict) path = path.replace(/\/$/, '')  
  if (path[0] === '/') return path //判断是否是根路由信息, "/"
  if (parent == null) return path // 不存在parent,说明当前 path就是为第一级路由信息,返回当前path路径即可
  return cleanPath(`${parent.path}/${path}`) // 存在,说明当前路由为childeren配置项的路由信息,拼接父路由和子路由path
}

那parent录音信息是在哪里传入的? 是在遍历第一次的路由信息时传入的。可以看递归时传入的参数:addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs) 。child参数即为当前子路由是所归属的父路由信息。也就是上面声明的RouteRecord 对象。

经过递归之后,record对象就会是这个样子

js 复制代码
 record = {
   path: '/',
   component: <Home>
 }
 record = {
   path: '/user/profile',
   component: <UserProfile>
 }
 record = {
   path: '/user/posts',
   component: <UserPosts>
 }
 record = {
   path: '/user/about',
   component: <About>
 }

在createRouteMap方法中会有一个pathMap对象用来保存扁平化之后的路由对象信息。

js 复制代码
  // 先读取oldPathMap 的值,oldPathMap是动态路由添加方法`addRoutes`提供的参数,oldPathMap就是在项目初始化声明的静态路由配置信息。项目初始化时肯定没有oldPathMap,所以创建了一个空对象。
  const pathMap: Dictionary<RouteRecord> = oldPathMap || Object.create(null)

回到addRouteRecord方法:

js 复制代码
  // 不能定义重复的路由信息,因为只会执行一次
  if (!pathMap[record.path]) {
    pathList.push(record.path)
    pathMap[record.path] = record
  }

可以看到还有另外一个数组对象,pathList ,这里用处就是保证根路由'/' 在数组中第一位。主要还是pathMap对象。

这里的pathMap对象就是扁平化之后的数据:

js 复制代码
pathMap = {
  '/': record,
  '/user/profile': record,
  '/user/posts': record,
  '/user/about': record,
  ...
  ...
}

其实还有一个nameMap,其原理跟pathMap一样,不断递归得到一个扁平化后的nameMap路由信息,只不过这时候对应的信息由原先path 映射 组件 换成了 name 映射组件。

到这里,由映射表创建已经完成,其实就是树的深度优先遍历的过程,最后将用户的路由配置扁平化成一个包含所有路由信息的对象。

相关推荐
todoitbo27 分钟前
本来只想给内网开个口子,Natapp 却多给了一道防护
前端·javascript·vue.js·内网穿透·网络防护·natapp
雪芽蓝域zzs1 小时前
JS 项目转 TS 项目(Vue3 + 若依这类后台项目)完整改造清单
前端·vue.js
鶴哥只手遮天14 小时前
Vue 3 核心源码深度剖析系列(v3.5.44)-第 1 章 项目全景:monorepo 架构与工程化体系
前端·vue.js·开源
OpenTiny社区16 小时前
多会话 + 流式输出怎么落地?TinyRobot Kit 对话数据层深度解析
前端·vue.js
破烂三角裤18 小时前
Vue 3 自动生成汉字拼音:pinyin-pro、Ruby 标签
vue.js
顽疲19 小时前
java vue springboot 养老系统源码详解:退住 care_out 审批结算与腾床事务实践
java·vue.js·spring boot·vue养老·springboot养老·java养老
雪芽蓝域zzs19 小时前
第 10 节:地图点击逆地理编码(点击地图获取经纬度 + 地址)
vue.js
斯内普吖19 小时前
(开源)线上花店系统实战指南 基于 Java + SSM + Vue + MySQL
java·vue.js·mysql·开源
溪语流沙20 小时前
【Web全栈进阶】Vue Router + Pinia:SPA路由与全局状态
前端·javascript·vue.js
u01110267521 小时前
WebP 转成 PNG 后为什么更大?图片格式转换不等于压缩
前端·javascript·vue.js