Vue Router 路由实现原理实现原理

Vue Router 路由实现原理

 

一、概念

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

 

二、实现方式

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

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

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

 

  Vue 中,它是通过 mode 这一参数控制路由的实现模式:

 

const router=new VueRouter({
    mode:'history',
    routes:[...]
})

 

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

  

 源码

 

  

  mode 参数:

    1.默认 hash

    2. history。如果浏览器不支持 history 新特性,则采用 hash

    3. 如果不在浏览器环境下,就采用 abstract(Node环境下)

    

 

  mode 区别:

    1. mode:"hash"  多了 “#”

http://localhost:8080/#/login

    

    2.mode:"history" 

http://localhost:8080/recommend

  

  HashHistory:

    hash("#") 的作用是加载 URL 中指示网页中的位置。

    # 本身以及它后面的字符称职位 hash,可通过 window.location.hash 获取

    特点:

      1. hash 虽然出现在 url 中,但不会被包括在 http 请求中,它是用来指导浏览器动作的,对服务器端完全无用,因此,改变 hash 不会重新加载页面。

      2. 可以为 hash 的改变添加监听事件:

      window.addEventListener("hashchange",funcRef,false)

      3. 每一次改变 hash(window.localtion.hash),都会在浏览器访问历史中增加一个记录。

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

 

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

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

  

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

  

 HashHisttory.push()

 

    

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

1
$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() //更新视图
复制代码

 

    transitionTo() 方法是父类中定义的是用来处理路由变化中的基础逻辑的,push() 方法最主要的是对 window 的 hash 进行了直接赋值:

  

window.location.hash=route.fullPath

 

    hash 的改变会自动添加到浏览器的访问历史记录中。
    那么视图的更新是怎么实现的呢,我们来看看父类 History 中的 transitionTo() 方法:

    

复制代码
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  const route = this.router.match(location, this.current)
  this.confirmTransition(route, () => {
    this.updateRoute(route)
    ...
  })
}

updateRoute (route: Route) {
  
  this.cb && this.cb(route)
  
}

listen (cb: Function) {
  this.cb = cb
}
复制代码

 

    可以看到,当路由变化时,调用了Hitory中的this.cb方法,而this.cb方法是通过History.listen(cb)进行设置的,回到VueRouter类定义中,找到了在init()中对其进行了设置:

    

复制代码
init (app: any /* Vue component instance */) {
    
  this.apps.push(app)

  history.listen(route => {
    this.apps.forEach((app) => {
      app._route = route
    })
  })
}
复制代码

 

 

    HashHistory.replace()

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

 

 HashHisttory.replace()

      

 

 

  HTML5History

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

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

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

    

    stateObject:当浏览器跳转到新的状态时,将触发 Popstate 事件,该事件将携带这个 stateObject 参数的副本

    title:所添加记录的标题

    url:所添加记录的 url

    

    这2个方法有个共同的特点:当调用他们修改浏览器历史栈后,虽然当前url改变了,但浏览器不会立即发送请求该url,这就为单页应用前端路由,更新视图但不重新请求页面提供了基础

    

    1.push

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

    2.replace

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

    3.监听地址变化

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


    
 View Code

 

 

  两种模式比较

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

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

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

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

posted @ 2020-02-07 07:31  Curedfisher  阅读(953)  评论(0编辑  收藏  举报