浅谈vue-router原理

155 阅读4分钟

在一般项目开发中,我们会把路由单独写在routes.js

const routes = [
  {
    path: '/',
    redirect: '/recommend'
  },
  {
    path: '/recommend',
    component: () => import('../components/recommend/view.vue')
  },
  {
    path: '/singer',
    component: () => import('../components/singer/view.vue')
  },
  {
    path: '/rank',
    component: () => import('../components/rank/view.vue')
  },
  {
    path: '/search',
    component: () => import('../components/search/view.vue')
  }
]
export default routes

那么vue-router的原理是什么呢?

vue-router是通过hash与History interface两种方式实现前端路由,更新视图但不重新请求页面,是前端路由原理的核心之一,目前在浏览器环境中这一切功能的实现主要有两种方式。

1.hash 利用URL中的hash("#")

2.利用History interface在HTML5中新增的方法

hash模式的实现原理

早期的前端路由的实现就是基于location.hash来实现的。原理很简单,location.hash 的值就是URL中# 后面的内容。比如下面这个网站,它的 location.hash 的值为 '#search':

https://www.taobao.com#search

hash 路由模式的实现主要是基于下面几个特性:

1.URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;

2.hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;

3.可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;

4.我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。

history 模式的实现原理

HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState()和history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:

window.history.pushState(null, null, path);window.history.replaceState(null, null, path);

history 路由模式的实现主要基于存在下面几个特性:

1.pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;

2.我们可以使用 pushState 事件来监听 url 的变化,从而对页面进行跳转(渲染);

3.history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。

那么,我们在开发中具体选择哪种路由模式呢?

在vue-router中,提供了mode参数来决定采用哪一种方式,选择流程如下:

mode参数:

  • hash(默认)
  • history(如果浏览器不支持history新特性,则采用hash方式)
  • 如果不在浏览器环境则使用abstract(node环境下)

编辑切换为居中

添加图片注释,不超过 140 字(可选)

router/index.js文件

import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'

Vue.use(Router)

export default new Router({
  // mode: 'history',
  routes
})

1.mode:'hash',多了“#”

http://localhost:8080/#/recommend

2.mode:'history'

http://localhost:8080/recommend

进过验证,两种模式打开的是同一个页面。

当你选择mode类型之后,程序会根据你选择的mode 类型创建不同的history对象(HashHistory或HTML5History或AbstractHistory),我们看看源码就知道了

 // 根据mode确定history实际的类并实例化    

// 根据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}`)
    }
}

什么是HashHistory,什么是HTML5History,有什么区别呢?

HashHistory

HashHistory真是身怀绝技,会很多东西。特别是替换路由特别厉害。还可以通过不同的方式,一个是 push ,一个是 replace .

两个方法:HashHistory.push() 和 HashHistory.replace()

**HashHistory.push() **将新路由添加到浏览器访问历史的栈顶

添加图片注释,不超过 140 字(可选)

从设置路由改变到视图更新的流程:

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

解析:

1 $router.push() //调用方法

2 HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash= XXX)

3 History.transitionTo() //监测更新,更新则调用History.updateRoute()

4 History.updateRoute() //更新路由

5 {app._route= route} //替换当前app路由

6 vm.render() //更新视图

HashHistory.replace()

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

编辑切换为居中

添加图片注释,不超过 140 字(可选)

replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  this.transitionTo(location, route => {
    replaceHash(route.fullPath)
    onComplete && onComplete(route)
  }, onAbort)
}
  
function replaceHash (path) {
  const i = window.location.href.indexOf('#')
  window.location.replace(
    window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
  )
}

领教了HashHistory的超能力,接下来看下

HTML5History

History interface是浏览器历史记录栈提供的接口,通过back(), forward(), go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。 从HTML5开始,History interface有进一步修炼:pushState(), replaceState() 这下不仅是读取了,还可以对浏览器历史记录栈进行修改:

window.history.pushState(stateObject, title, URL)window.history.replaceState(stateObject, title, URL)
  • stateObject: 当浏览器跳转到新的状态时,将触发popState事件,该事件将携带这个stateObject参数的副本
  • title: 所添加记录的标题
  • URL: 所添加记录的URL

1.push 与hash模式类似,只是将window.hash改为history.pushState 2.replace 与hash模式类似,只是将window.replace改为history.replaceState 3.监听地址变化 在HTML5History的构造函数中监听popState(window.onpopstate)

HashHistory和HTML5History既然都这么强大,那么我们在开发过程中该如何选择呢?

  • pushState设置的新URL可以是与当前URL同源的任意URL;而hash只可修改#后面的部分,故只可设置与当前同文档的URL
  • pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串
  • pushState可额外设置title属性供后续使用
  • history模式则会将URL修改得就和正常请求后端的URL一样,如后端没有配置对应/user/id的路由处理,则会返回404错误