写点什么

【VueRouter 源码学习】第五篇 - 两种路由模式的设计

用户头像
Brave
关注
发布于: 4 小时前
【VueRouter 源码学习】第五篇 - 两种路由模式的设计

一,前言


上篇,介绍创建路由映射表,主要包含以下内容:


  • 创建路由初始化方法 init;

  • 路由匹配器的创建 createMatcher;

  • match 方法 和 addRoutes 方法的实现;


本篇,继续介绍两种路由模式的设计;


二,承上启下

1,前文回顾


  • 在 VueRouter 实例化时,通过路由匹配器 createMatcher 对路由配置进行扁平化处

  • 在路由匹配器中,创建了路由插件的两个核心方法:match 和 addRoutes;

2,本篇介绍


有了路由配置的映射关系之后,还需要实现根据不同路径跳转并显示对应的组件;本篇,主要介绍两种路由模式的设计及初始化操作;


三,路由跳转的实现

1,路由模式的处理


根据路由选项中所配置的不同路由模式,需要进行不同的处理;


路由模式共有三种,主要介绍 Hash 和 History 两种模式:

// index.js
import HashHistory from './history/hash';import BrowserHistory from './history/history';
class VueRouter { constructor(options) { // 路由配置的扁平化处理 this.matcher = createMatcher(options.routes || []); // 根据不同的路由模式,生成对应的处理实例 options.mode = options.mode || 'hash'; // 默认hash模式 switch (options.mode) { case 'hash': this.history = new HashHistory(this); break; case 'history': this.history = new BrowserHistory(this); break; } } // 路由初始化方法,供 install 安装时调用 init(app) {}}VueRouter.install = install;export default VueRouter;
复制代码


路由切换时,需要通过 matcher 进行规则匹配,所以在创建两种理由模式的类时,需要传入 this,即当前路由实例;

2,创建两种路由模式类:HashHistory 和 BrowserHistory


创建两种路由模式对应的类:HashHistory 和 BrowserHistory,他们均继承自路由的公共逻辑处理 History 类;


History 类:HashHistory 和 BrowserHistory 的父类,包含两种路由模式的公共逻辑处理:

// history/base.jsclass History {  constructor(router) {    this.router = router;  // 存储子类传入的 router 实例  }}
export { History }
复制代码


HashHistory 类:继承自 History 类,对应 Hash 模式下方法的实现:

// history/hash.jsimport { History } from "./base";
class HashHistory extends History{ constructor(router) { super(router); // 调用父类构造方法,并将 router 实例传给父类 this.router = router; // 存储 router 实例,共内部使用 }}
export default HashHistory
复制代码


BrowserHistory 类:继承自 History 类,对应 History 模式下方法的实现:

// ---------------------------- // // history/hash.jsimport { History } from "./base";
class BrowserHistory extends History{ constructor(router) { super(router); // 调用父类构造方法,并将 router 实例传给父类 this.router = router; // 存储 router 实例,共内部使用 }}
export default BrowserHistory
复制代码

3,Hash 模式的路径处理


Hash 模式下,路径后会默认携带'/'符号,HashHistory 初始化时进行处理;

import { History } from "./base";
function ensureSlash() { // location.hash 存在兼容性问题,可根据完整 URL 判断是否包含'/' if (window.location.hash) { return; } window.location.hash = '/'; // 如果当前路径没有hash,默认为 /}
class HashHistory extends History { constructor(router) { super(router); this.router = router; // Hash 模式下,对URL路径进行处理,确保包含'/' ensureSlash(); }}export default HashHistory
复制代码

4,父类和子类的设计


  • 无论是哪一种路由模式 HashHistory 或 BrowserHistory 都需要通过当前的路径进行路由匹配和跳转

  • 路径获取:Hash 模式获取当前路径 hash 值,history 模式获取当前路径 path 值;

  • 路由监听:Hash 模式监听 hashchange 事件,history 模式监听 popState 事件;


父类和子类的方法设计:


  • base.js:

  • transitionTo 根据路由进行匹配跳转;

  • hash.js:

  • getCurrentLocation:获取当前路径 hash 值;

  • setupListener:监听 hashchange 事件

  • history.js:

  • getCurrentLocation:获取当前路径 path 值;

  • setupListener:监听 popState 事件

5, 父类和子类方法的实现


父类 History:

// base.js
class History { constructor(router) { this.router = router; } // 根据路径进行路由匹配,并添加路径改变的监听器 transitionTo(location, onComplete) { onComplete && onComplete(); }}
export { History }
复制代码


Hash 模式的子类 HashHistory:

import { History } from "./base";
class HashHistory extends History{ constructor(router){ super(router); this.router = router; ensureSlash(); } getCurrentLocation(){ // 获取路径的 hash 值 return getHash(); } setupListener(){ // 当 hash 值变化时,获取新的 hash 值,并进行匹配跳转 window.addEventListener('hashchange',()=>{ this.transitionTo(getHash()); }) }}
export default BrowserHistory
复制代码


History 模式的子类 HashHistory:

class BrowserHistory extends History{    setupListener(){        // 当路径变化时,拿到新的 hash 值,并进行匹配跳转        window.addEventListener('popState',()=>{            this.transitionTo(getHash());        })    }}
export default BrowserHistory
复制代码

6,路由初始化 init 方法

// index.js
class VueRouter { constructor(options) { this.matcher = createMatcher(options.routes || []); options.mode = options.mode || 'hash'; switch (options.mode) { case 'hash': this.history = new HashHistory(this); break; case 'history': this.history = new BrowserHistory(this); break; } } // 监听 hash 值变化,跳转到对应的路径中 init(app) { // 当前的history实例:可能是HashHistory,也可能是BrowserHistory; const history = this.history; // 设置监听器:内部调用的是不同子类中的实现 const setUpListener = () => { history.setupListener(); } // 初始化时,获取当前hash值进行跳转, 并设置监听器 history.transitionTo( history.getCurrentLocation(), setUpListener ) }}VueRouter.install = install;
export default VueRouter;
复制代码



四,结尾


本篇,介绍了两种路由模式的设计及初始化操作,主要涉及以下几个点:


  • 创建两种路由模式类;

  • 父类和子类继承方法的设计;

  • 路由初始化 init 处理逻辑;


下一篇,路由匹配的实现;

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【VueRouter 源码学习】第五篇 - 两种路由模式的设计