(解析)单页应用路由实现没那么难--Vue

前言

单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。简单来讲就是用户只须要加载一次页面就能够再也不请求,当点击其余子页面时只会有相应的URL改变而不会从新加载。
咱们能够将实现路由的过程分为两部分:javascript

  1. 更新URL页面不刷新
  2. 监听URL的变化,执行页面替换逻辑

如今主流有2种实现方案:html

  1. history.pushState等触发popstate事件
  2. location.hash的变化触发hashchange事件

接下来咱们一步一步看Vue-router如何实现的vue

Vue-router源码解剖

构造器

请各位同窗翻到 src/index.js 第18行html5

export default class VueRouter {
  static install: () => void;
  static version: string;

  app: any;
  apps: Array<any>;
  ready: boolean;
  readyCbs: Array<Function>;
  options: RouterOptions;
  mode: string;
  history: HashHistory | HTML5History | AbstractHistory;
  matcher: Matcher;
  fallback: boolean;
  beforeHooks: Array<?NavigationGuard>;
  resolveHooks: Array<?NavigationGuard>;
  afterHooks: Array<?AfterNavigationHook>;

  constructor (options: RouterOptions = {}) { 
    this.app = null
    this.apps = []
    this.options = options
    this.beforeHooks = []
    this.resolveHooks = []
    this.afterHooks = []
    this.matcher = createMatcher(options.routes || [], this)

    let mode = options.mode || 'hash'
    this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
    if (this.fallback) {
      mode = 'hash'
    }
    if (!inBrowser) {
      mode = 'abstract'
    }
    this.mode = mode

    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HashHistory(this, options.base, this.fallback)
        break
      case 'abstract':
        this.history = new AbstractHistory(this, options.base)
        break
      default:
        if (process.env.NODE_ENV !== 'production') {
          assert(false, `invalid mode: ${mode}`)
        }
    }
  }

构造器接收一个options参数java

clipboard.png

默认mode为 "hash",若是显示传入参数mode为"history",则进行 是否支持的"history"的判断web

this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false

supportsPushState方法 里面 判断了 是否为浏览器环境且当前浏览器版本支持historyvue-router

clipboard.png

options.fallback用来控制路由,在设置了mode为"history"可是当前浏览器环境不支持"history"的状况下是否应该回调判断,并从新设置mode为"hash"。
设置fallback为false本质上是为了让"router-link"在IE9上能够完整的页面刷新,若是是在hash模式下面不支持SSR,设置为false,会让那些在ie9服务端渲染的app更好用。(这段话没具体应用过)数组

clipboard.png

以后根据不一样的mode,来执行不一样的方案浏览器

clipboard.png

HTML5History

clipboard.png

构造器

构造器接收2个参数,router和base
router是在定义新路由的时候建立的对象
base若是没有传,则为undefined,
"?"这是经过一个名为flow的外部工具为javascript加上强类型检查的功能,不影响编译和运行。直接无视就好。
调动History的构造方法,History为 HTML5History,HashHistory,AbstractHistory的超类app

clipboard.png

在History的构造方法中,

clipboard.png

若是base为undefined,查找是否有base的元素,有就赋值,没有就'/'
以后

const expectScroll = router.options.scrollBehavior
    const supportsScroll = supportsPushState && expectScroll

    if (supportsScroll) {
      setupScroll()
    }

判断路由参数,是否控制路由页面滚动条行为

clipboard.png

监听popstate事件,跳转

clipboard.png

clipboard.png
获取当前location的值以后,

clipboard.png
进行路由的更新,好比当前的History对应哪一个路由

clipboard.png

clipboard.png
Html5History也添加了go,push,replace等方法用来路由跳转,

clipboard.png
先保存滚动条状态,以后可使用history的自带方法进行地址的改变
clipboard.png

更多详情请见MDN

未完待续

HashHistory

构造器

clipboard.png
调动History的构造方法,History为 HTML5History,HashHistory,AbstractHistory的超类
判断当前hash地址

clipboard.png

若是开头不是/#,将当前location按照hash格式化

clipboard.png

根据href获取当前hash,若是没有匹配到'#'返回空字符串。
初始化地址栏hash后

clipboard.png

clipboard.png

监听popstate事件,替换路由,控制滚动条行为

导航守卫

clipboard.png

在registerHook将设置的守卫入栈
在每次跳转的时候,递归守卫集合,将触发的守卫进行解析和执行。

clipboard.png

clipboard.png

AbstractHistory

构造器

clipboard.png
相对于上两种方法,AbstractHistory看起来要简单不少,这种模式是用于 Node.js 环境的,通常场景也就是在作测试的时候。可是在实际项目中其实还可使用的,利用这种特性仍是能够很方便的作不少事情的。(我没有用过)
由于不涉及和浏览器地址相关记录关联在一块儿;总体流程依旧和 HashHistory 是同样的,只是这里经过数组来模拟浏览器历史记录堆栈信息。

clipboard.png

更新历史堆栈信息,更新当前所处位置
等等,除了不使用浏览器的history对象,其余的和html5history模式差很少。

小结

vue-router的源码剖析到这里就结束了,大概流程是这个样子,得益于开发人员代码的简洁性及可读性,咱们阅读起来障碍仍是没有那么多,难度也没有那么大,总体逻辑不复杂,可是想要把不少不复杂的细节,整合到一块儿,认真到细节,才是程序设计的美学。

相关文章
相关标签/搜索