【手写 VueRouter 源码】第四篇 - 创建路由映射表

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一前言

上篇介绍路由插件 install 的实现主要包含以下内容

  • 创建 vue-router 目录结构
  • 插件引入入口 index.js 逻辑实现
  • 插件安装入口 install.js 逻辑实现
  • 为所有组件混入 router 实例

本篇创建路由映射表


二承上启下

1前文回顾

  • 完成了路由插件的配置、注册并导出 router 路由实例
  • 在 Vue 初始化时将 router 路由实例注册到了 Vue 实例上即根组件Vue.app
  • 自建 vue-router 插件目录结构导入入口 index.js安装逻辑 install.js
  • 执行 Vue.use 安装插件时通过 Vue.mixin 向所有组件混入导出的 router 实例
  • 备注在组件创建前通过全局混入 beforeCreate 生命周期函数将 router 实例从根组件逐层地共享给其他子组件

至此完成了插件的创建创建了插件导入入口和 install 安装方法

2本篇介绍

但是目前还只是一个插件的“空壳”引入插件后并没有进行插件功能的初始化

所以需要在插件安装时执行的 install 方法中完成插件的初始化操作

路由插件初始化的主要逻辑如下

  • 在 VueRouter 实例化时接收外部传入的路由配置 optionsnew Router(options)构造函数内部通过 createMatcher 路由匹配器对其进行处理
  • 在路由匹配器 createMatcher 中将路由配置处理为便于匹配的扁平化结构对象 matcher
  • 在路由匹配器中创建路由插件的两个核心方法match 和 addRoutes
  • 在 VueRouter 类中创建路由初始化方法 init当执行Vue.use安装路由插件在 install 方法中处理根组件时执行路由的 init 初始化操作将根实例 Vue.app 传入 init 方法中

三路由插件的初始化

1路由实例化时的初始化逻辑

new VueRouter进行路由的初始化时在构造函数中需要完成以下几件事

  • 创建路由匹配器 createMatcher 处理路由配置将嵌套数组处理为扁平对象便于路由匹配操作
  • 创建 match 方法用于通过路由规则匹配到对应的组件
  • 创建 addRoutes 方法用于动态的添加路由匹配规则

2路由安装时的初始化逻辑 init

// index.js

import install from './install'

class VueRouter {
    constructor(options) {  // 传入配置对象 options

    }
    // 路由初始化方法供 install 安装时调用
    init(app) {
            
    }
}
VueRouter.install = install;

export default VueRouter;

在根组件中调用路由实例上的 init 方法完成插件的初始化

// install.js

export let _Vue;

/**
 * 插件安装入口 install 逻辑
 * @param {*} Vue     Vue 的构造函数
 * @param {*} options 插件的选项
 */
export default function install(Vue, options) {
  _Vue = Vue;// 抛出 Vue 供其他文件使用
  
  // 混入所有组件都能够通过 this._routerRoot._router 获取到同一个 router 实例
  Vue.mixin({
    beforeCreate() {
      if (this.$options.router) {// 根组件
        this._routerRoot = this;
        this._router = this.$options.router; 
        // 在根组件中调用路由实例上的 init 方法完成插件的初始化
        this._router.init(this); // this 为根实例
      } else { // 子组件
        this._routerRoot = this.$parent && this.$parent._routerRoot;
      }
    }
  });
}

四路由匹配器函数 createMatcher

1创建路由匹配器函数

新建 create-match.js 文件创建路由匹配器函数 createMatcher

// create-match.js

/**
 * 路由匹配器函数
 *  对路由配置进行扁平化处理
 *  addRoutes动态添加路由匹配规则
 *  match根据路径进行路由匹配
 * @param {*} routes 
 * @returns 返回路由匹配器的两个核心方法 addRoutes、match
 */
export default function createMatcher(routes) {
    // 将嵌套数组的路由配置处理为便于匹配的扁平结构
    // 创建 match 方法根据路径进行路由匹配
    // 创建 addRoutes 方法动态添加路由匹配规则
}

VueRouter构造函数中使用路由匹配器函数 createMatcher将嵌套数组类型的路由配置进行扁平化处理

// index.js

import install from './install'
import createMatcher from './create-matcher';  // 导入匹配器

class VueRouter {
    constructor(options) {
        // 路由匹配器-处理路由配置将树形结构的嵌套数组转化为扁平化结构的对象便于后续的路由匹配
        // 路由匹配器返回两个核心方法match、addRoutes
        this.matcher = createMatcher(options.routes || []);// options.routes 默认[]
    }
    
    /**
     * 路由初始化方法供 install 安装时调用
     * @param {*} app 根组件实例在处理根组件时被调用
     */
    init(app) { 
            
    }
}

VueRouter.install = install;

export default VueRouter;

处理完成后路由实例上的 matcher 属性将存储扁平化结构的全部路由匹配规则用于后续路由匹配操作

2路由配置的扁平化处理

在 create-route-map.js 文件中处理路由配置

// create-route-map.js

/**
 * 路由配置扁平化处理
 *  支持初始化和追加两种情况
 * @param {*} routes     路由实例中的路由配置
 * @param {*} oldPathMap 路由规则映射表扁平化结构
 * @returns 新的路由规则映射表扁平化结构
 */
export default function createRouteMap(routes, oldPathMap){

  // 拿到当前已有的映射关系
  let pathMap = oldPathMap || Object.create(null);

  // 将路由配置 routes 依次加入到 pathMap 路由规则的扁平化映射表
  routes.forEach(route => {
    addRouteRecord(route, pathMap);
  });

  return {
    pathMap
  }
}

/**
 * 添加一个路由记录递归当前的树形路由配置
 *  先序深度遍历先把当前路由放进去再处理他的子路由
 * @param {*} route   原始路由记录
 * @param {*} pathMap 路由规则的扁平化映射表
 * @param {*} parent  当前路由所属的父路由对象
 */
function addRouteRecord(route, pathMap, parent){

  // 处理子路由时需要做路径拼接
  let path = parent ? (parent.path + '/' + route.path) : route.path
  
  // 构造路由记录对象还包含其他属性path、component、parent、name、props、meta、redirect...
  let record = {
    path,
    component: route.component,
    parent // 标识当前组件的父路由记录对象
  }

  // 查重路由定义不能重复否则仅第一个生效
  if (!pathMap[path]) { 
    pathMap[path] = record; // 将当前路由的映射关系存入pathMap
  }

  // 递归处理当前路由的子路由
  if (route.children) {
    route.children.forEach(childRoute => {
      addRouteRecord(childRoute, pathMap, record);
    })
  }
}

在路由匹配器中,调用 createRouteMap 方法进行路由配置的扁平化处理

import createRouteMap from "./create-route-map"

export default function createMatcher(routes) {
  //  路由配置的扁平化处理
  let { pathMap } = createRouteMap(routes);
}

3添加 match 方法

创建 match 方法用于通过路由规则匹配到对应的组件

import createRouteMap from "./create-route-map"

export default function createMatcher(routes) {

    //  路由配置的扁平化处理
    let { pathMap } = createRouteMap(routes);
    
    // 根据路径进行路由匹配
    function match(location) {
        let record = pathMap[location];
    }
    
    return {
        addRoutes, // 添加路由 
        match // 用于匹配路径
    }
}

4添加 addRoutes 方法

创建 addRoutes 方法用于动态的添加路由匹配规则

import createRouteMap from "./create-route-map"

export default function createMatcher(routes) {

    //  路由配置的扁平化处理
    let { pathMap } = createRouteMap(routes);
    
    // 根据路径进行路由匹配
    function match(location) {
        let record = pathMap[location];
    }
    
    /**
     * 动态添加路由匹配规则
     *  将追加的路由规则进行扁平化处理
     */
    function addRoutes(routes) {
        createRouteMap(routes,pathMap);
    }
    
    return {
        addRoutes, // 添加路由 
        match // 用于匹配路径
    }
}

应用场景在后台管理应用中部分菜单/路由是根据权限配置来决定的可以使用路由插件提供的 addRoutes 方法动态添加路由


五结尾

本篇介绍了路由映射表的创建主要包含以下内容

  • 路由安装的初始化init 方法
  • 路由初始化constructor 构造函数逻辑
  • 创建路由匹配器createMatcher
  • match方法 和 addRoutes方法的实现

下一篇介绍路由跳转的实现

// todo 本篇大多篇幅和主题介绍了路由匹配器和 VueRouter 构造函数中的处理逻辑init 方法的相关介绍可以考虑从本篇移除


维护日志

  • 20210821

    • 添加了“承上启下”部分总结前置知识点并引出本文要解决的问题
    • 修改了部分二级标题的命名使主题描述更加准确
    • 其他决定要有一个“承上启下”的部分作为开篇
      • 一来让读者能够快速了解与本文有关的前置知识点更好的将前后文串联起来
      • 二来强制自己做一次阶段性总结捋清叙事思路
      • “承上启下”部分命名仍需思考与“前言”部分有重复
  • 20210823

    • 调整了“路由安装”与“路由实例化”初始化先后顺序
    • 添加了诸多代码注释使关键逻辑清晰易懂
    • 添加 todo修改“结尾”与文章摘要
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: vue

“【手写 VueRouter 源码】第四篇 - 创建路由映射表” 的相关文章