VueRouter 构造函数

class VueRouter {
  constructor(options) {
    // 1.接收 RouterOptions
    //  routes 是路由配置表,是一个数组,每一项都是一个路由配置规则;
    //  mode、fallback 是跟路由模式相关的;

    // 2.创建匹配器的过程,并返回 match, addRoutes 方法
    //  match函数:匹配功能
    //  addRoutes函数:可以添加匹配,动态路由添加
    this.matcher = createMatcher(options.routes || []);

    // 3.创建路由模式,默认值是 hash 模式
    this.mode = options.mode || 'hash';
    switch (this.mode) {
      case 'hash':
        this.history = new HashHistory(this)
        break;
      case 'history':
        this.history = new BrowserHistory(this);
        break;
    }

    // 4.初始化钩子队列(这里只初始化 beforeHooks)
    this.beforeHooks = [];
  }
  match(location) {
  }
  init(app) { 
  }
  push(location) {
  }
  beforeEach(fn) {
  }
}

创建匹配器

JavaScript
JavaScript
import createRouteMap from './create-route-map'
import { createRoute } from './history/base'

export default function createMatcher(routes) {
  // 动态添加路由 规则就是 404 首页 所有人都能看到 先配好了
  // 登录了 => 再将对应的权限和之前的进行一个组合  (菜单全选 路由权限)

  // routes 是用户自己配置的,但是用起来不方便,利用 createRouteMap 生成 pathList、pathMap
  //  pathList:把所有的路由t,组成一个数组 => ['/', '/about', '/about/a', '/about/b', '/xxx']
  //  pathMap :把所有的路由,组成一个map => { '/': { }, '/about': { }, '/about/a': { } }
  let { pathList, pathMap } = createRouteMap(routes);

  // 通过用户输入路径,获取对应的匹配记录
  function match(location) {
    let record = pathMap[location];// 获取对应的记录
    // /about/a  => matched:[/about,/a]
    return createRoute(record, { path: location })
  }

  // 动态格式化路由
  function addRoutes(routes) {
    // routes 动态添加的路由
    createRouteMap(routes, pathList, pathMap)
  }
  return { match, addRoutes };
}
const addRouteRecord = (route, pathList, pathMap, parentRecord) => {
  // 多层级路由时,做拼接
  let path = parentRecord ? `${parentRecord.path}/${route.path}` : route.path;
  // 根据当前路由产生一个记录 path/component
  let record = { path, component: route.component, parent: parentRecord /* 记录父路径 */ };

  // 防止用户编写路由时有重复的,不去覆盖
  if (!pathMap[path]) {
    pathMap[path] = record;
    pathList.push(path);
  }

  /// 要将子路由也放到对应的 pathMap 和 pathList
  if (route.children) {
    route.children.forEach(r => {
        addRouteRecord(r, pathList, pathMap, record);
    });
  }
}

// 具备初始化和新增的功能
function createRouteMap(routes, oldPathList, oldPathMap) {
  let pathList = oldPathList || [];
  let pathMap = oldPathMap || {};
  routes.forEach(route => { addRouteRecord(route, pathList, pathMap) });
  return { pathList, pathMap };
}
export default createRouteMap;

路由模式

JavaScript
JavaScript
// 根据匹配到的记录来计算匹配到的所有的记录  
export const createRoute = (record, location) => {
  let matched = []
  if (record) {
    while (record) {
      matched.unshift(record);
      record = record.parent; // 通过当前记录找到所有的父亲 
    }
  }
  // {path:'/',matched:[{}]}
  return { ...location, matched };
}
const runQueue = (queue, iterator, complete) => {
  function next(index) {
    if (index >= queue.length) {
      return complete();
    }
    let hook = queue[index]; // 取出hook钩子函数
    iterator(hook, () => next(index + 1)); // 就是遍历的过程
  }
  next(0);
}

// 这个 current 就是一个普通的变量,希望 current 变化了可以更新视图
export default class History {
  constructor(router) {
    this.router = router;
    // 这个代表的是,当前路径匹配出来的记录
    // /about/a 匹配=> {path:'/about',component:about}、{path:'/about/A',component:A}
    // this.current = {path:'/',matched:[]}
    this.current = createRoute(null, { path: '/' }); // 默认值
  }
  transitionTo(location, complete) {
    // 获取当前路径匹配出对应的记录,当路径变化时获取对应的记录 => 渲染页面 (router-view实现的)
    // 通过路径拿到对应的记录,有了记录之后,就可以找到对象的匹配
    // 当路径变化后 current 属性会进行更新操作
    let current = this.router.match(location);

    // 防止重复点击 不需要再次渲染
    // 匹配到的个数和路径都是相同的,就不需要再次跳转了
    if (this.current.path == location && this.current.matched.length === current.matched.length) {
      return;
    }

    let queue = this.router.beforeHooks; // 钩子函数队列
    const iterator = (hook, next) => { hook(current, this.current, next) };
    runQueue(queue, iterator, () => {
      this.current = current; // 这个 current 只是响应式的,他的变化不会更新 _route
      this.cb && this.cb(current); // 手动更新 _route 的回调函数
      complete && complete(); // 监听浏览器 url 变化的事件的初始化函数
    });
  }
  listen(cb) { // 保存回调函数
    this.cb = cb;
  }
}
import History from './base';

const ensureSlash = () => {
  // window.location.href.slice('#')[1]
  if (window.location.hash) {
    return;
  }
  window.location.hash = '/';
}

export default class HashHistory extends History {
  constructor(router) {
    super(router);
    this.router = router;
    ensureSlash(); // 如果没有 hash 值,应该跳转到 首页 #/
  }
  getCurrentLocation() {
    return window.location.hash.slice(1);
  }
  setupListener() {
    window.addEventListener('hashchange', () => {
      // 再次执行匹配操作
      this.transitionTo(this.getCurrentLocation())
    })
  }
}
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. VueRouter 构造函数
  2. 2. 创建匹配器
  3. 3. 路由模式