Vue Router 路由实现原理

目录

  • 一、概念
  • 二、两种实现方式
    • HashHistory
      • 简介
      • 特点
      • 方法
        • HashHistory.push()
        • HashHistory.replace()
    • HTML5History
      • 简介
      • 特点
      • 方法
        • history.pushState()
        • history.replaceState()
  • 三、两种模式比较

一、概念

通过改变URL,在不重新请求页面的情况下,更新页面视图。

二、两种实现方式

更新视图但不重新请求页面,是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有2种方式:

  • Hash—利用URL中的hash(“#”);
  • 利用History interface在HTML5中新增的方法。

mode参数

Vue中,通过mode这一参数来决定采用哪一种方式,选择流程如下:
Vue Router 路由实现原理_第1张图片

  • 默认为hash

  • 使用history时,进行如下设置(如果浏览器比支持history新特性,则采用hash方式)

const router=new VueRouter({
    mode:'history',//设置路由实现方式为history
    routes:[...]
})
  • 如果不在浏览器环境下则使用abstract(node环境下)

创建VueRouter的实例对象时,mode以构造参数的形式传入,如下代码:

src/index.js

export default class VueRouter{
  mode: string; // 传入的字符串参数,指示history类别
  history: HashHistory | HTML5History | AbstractHistory; // 实际起作用的对象属性,必须是以上三个类的枚举
  fallback: boolean; // 如浏览器不支持,'history'模式需回滚为'hash'模式
  
  constructor (options: RouterOptions = {}) {
    
    let mode = options.mode || 'hash' // 默认为'hash'模式
    this.fallback = mode === 'history' && !supportsPushState // 通过supportsPushState判断浏览器是否支持'history'模式
    if (this.fallback) {
      mode = 'hash'
    }
    if (!inBrowser) {
      mode = 'abstract' // 不在浏览器环境下运行需强制为'abstract'模式
    }
    this.mode = mode

    // 根据mode确定history实际的类并实例化
    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}`)
        }
    }
  }

  init (app: any /* Vue component instance */) {
    
    const history = this.history

    // 根据history的类别执行相应的初始化操作和监听
    if (history instanceof HTML5History) {
      history.transitionTo(history.getCurrentLocation())
    } else if (history instanceof HashHistory) {
      const setupHashListener = () => {
        history.setupListeners()
      }
      history.transitionTo(
        history.getCurrentLocation(),
        setupHashListener,
        setupHashListener
      )
    }

    history.listen(route => {
      this.apps.forEach((app) => {
        app._route = route
      })
    })
  }

  // VueRouter类暴露的以下方法实际是调用具体history对象的方法
  push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.history.push(location, onComplete, onAbort)
  }

  replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.history.replace(location, onComplete, onAbort)
  }
}

源码

HashHistory

简介

hash(“#”)的作用是加载URL中指示的网页的位置。
#本身以及它后面的字符,可以通过window.location.hash获取。

特点

  1. 通过hash方式的,路由地址会有#
    举例:
http://localhost:8080/#/a
  1. #后面的内容不会传给服务端,也就是说不会重新刷新页面。并且路由切换的时候也不会重新加载页面。因此改变hash不会重新加载页面
  2. 每一次改变 hash(window.localtion.hash),都会在浏览器访问历史中增加一个记录。
    举例:
http://localhost:8080/#/a 
//变为 如下地址,浏览器访问历史中会增加一个记录
http://localhost:8080/#/b 
  1. 可以为 hash 的改变添加监听事件:
  window.addEventListener("hashchange",funcRef,false)  

利用hash的以上特点,就可以来实现“前端路由”更新视图但不重新请求页面的功能了。

方法

HashHistory 拥有两个方法,一个是HashHistory.push(), 一个是HashHistory.replace()
HashHistory.push()

将路由添加到浏览器访问历史的栈顶

如图:
Vue Router 路由实现原理_第2张图片
从设置路由改变到视图更新的流程:

$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()

详解:

$router.push() //调用方法
HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash = XXX)
History.transitionTo() //监测更新,更新则调用History.updateRoute()
History.updateRoute() //更新路由
{app._route= route} //
vm.render() //更新视图
HashHistory.replace()

replace()方法与push()方法不同之处在于,他并不是将新路由添加到浏览器访问历史的栈顶,而是替换掉当前路由。

如图:
Vue Router 路由实现原理_第3张图片

HTML5History

简介

History interface是浏览器历史记录栈提供的接口,通过back()forward()go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。

从Html5开始,History interface提供了两个新的方法:pushState(),replaceState()提供了2个新方法:pushState(),replaceState()使得我们可以对浏览器历史记录栈进行修改

window.history.pushState(stateObject,title,url)
  window.history,replaceState(stateObject,title,url)

参数分析:

  • stateObject:当浏览器跳转到新的状态时,将触发Popstate事件,该事件将携带这个stateObject参数的副本
  • title:所添加记录的标题
  • url:所添加记录的url

特点

  • 通过history模式,实现单页路由的URL没有# ,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。
http://localhost:8080/a
  • 为了避免这种情况,所以history路由实现方式需要服务器的支持,需要把所有的路由都定向到根页面。

  • 在HTML5History的构造函数中监听使用popState(window.onpopstate)

方法

history.pushState()

与hash模式类似,只是将window.hash()改为history.pushState()

history.replaceState()

与hash模式类似,只是将window.replace()改为history.replaceState()

三、两种模式比较

  1. pushState设置的新的URL可以是与当前URL同源的任意URL;而hash只可修改#后边的部分,故只可设置与当前文档同文档的URL

  2. pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串

  3. pushState可额外设置title属性后供后续使用

  4. history模式则会将URL修改得就和正常请求后端的URL一样,如后端没有配置对应/user/id的路由处理,则会返回404错误

Vue Router 路由实现原理_第4张图片

你可能感兴趣的:(Vue.js,vue.js,前端,哈希算法,开发语言,vue)