vue-router路由原理

时间:2021-01-24 21:54:02

Vue-router路由原理

目前实现路由的方式有两中,vue通过参数mode来设置,默认是hash模式。

  1. 利用URL中的hash(‘#’)来实现
  2. 利用History interface在HTML5中新增的方法

history对应的是HTML5History对象,hash对应的是HashHistory对象,abstract对应的是AbstractHistory对象。在初始化对应的history之前,会对mode做一些校验:若浏览器不支持HTML5History方式(通过supportsPushState遍历判断),则mode设为hash,若不是在浏览器环境下运行,则mode设为abstract

VueRouter类中的onReady(),push()等方法只是一个代理,实际是调用的具体history对象的对应方法,在init()方法中初始化时,也是根据history对象具体的类别执行不同操作

HashHistory

Hash(‘#’)符号的本来作用是加在URL指示网页中的位置,#本身以及它后面的字符称之为hash,通过window.location.hash属性来获取

Hash虽然出现在url中,但不会被包括在http请求中,它是用来指导浏览器动作的,对服务端完全无用,因此改变hash不会重新加载页面,如果是手动在url添加hash,这时可以为hash的改变添加监听事件

Window.addEventListener(‘hashchange’,funcRef,false)

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

执行vue里的push()方法最主要的是对window的hash进行直接赋值

Window.location.hash = route.fullPath

$router.push()  -> HashHistory.push()  -> History.transitionTo()  -> History.updateRoute() -> vm.render()

HashHistory.replace()

Replace()方法与push()方法不同之处在于,它并不是将新路由添加到浏览器访问历史栈顶,而是替换掉当前的路由,背后它实际上是调用window.location.replace方法将路由进行替换

监听地址栏

VueRouter.push()和VueRouter.replace()是可以在vue组件的逻辑代码中直接调用的,除此之外在浏览器中,用户还可以手动在浏览器地址栏中输入url,因此还需要监听浏览器地址栏中路由的变化,并具有与通过代码调用的相同响应行为,在History中通过setupListeners监听hashchange实现:

Window.addEventListener(‘hashchange’,function () {}),当在浏览器地址栏直接输入路由相当于代码调用了replace()方法

HTML5History

History interface是浏览器历史记录栈提供的接口,back(),forward(),go()等方法,HTML5提供了pushState(),replateState()这两个方法

Window.history.pushState(stateObject,title,url)

Window.history.replateState(stateObject,title,url)

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

title:所添加记录的标题

url:所添加记录的url

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

History与hash模式基本类似,只不过将对window.location.hash()直接进行赋值window.location.replate()改为调用了window.location.history.pushState()和window.location.replateState()方法,而HTML5History中添加对修改浏览器地址栏url的监听popstate是直接在构造函数中执行的

Window.addEventListeners(‘popstate’,e => {// 执行相应的功能})

HTML5History需要浏览器的支持,vue是通过supportPushState()来检查

除这两中模式外,vue-router还未非浏览器环境准备了一个abstract模式,原理是用一个数组stack模拟除浏览器历史记录栈的功能

两中模式比较

调用history.pushState()相比直接修改hash主要有以下优势:

  1. pushState设置的新url可以是与当前url同源的任意url,而hash只可修改#后面的部分,故可设置与当前同文档的url
  2. pushState设置的新url可以与当前url一模一样,这样也会把记录添加到栈中,而hash设置的新的值必须与原来不一样才会触发记录添加到栈中
  3. pushState通过stateObject可以添加任意类型的数据记录中,而hash只可添加短字符串
  4. pushState可额外设置title属性供后续使用

history模式问题

对于单页面应用来说,理想的使用场景是仅仅在进入用用时加载index’.html,后续在网络操作通过ajax完成,不会根据url重新请求页面,但是如果用户直接在地址栏中输入并回车,浏览器重启重新加载等特殊情况

Hash模式仅仅改变hash部分的内容,而hash部分是不会包含在http请求中的,就是#以及后面的是不会包含在请求当中的

http://baidu.com/#user/id      //如请求,只会发送http://baidu.com

所以hash模式下遇到根据url请求页面不会有问题

而history模式则将url修改的就和政策请求后端的url一样,history不带#。http://baidu.com/user/id

如果这种向后端发送请求的话,后端没有配置对应/user/id的路由处理,会返回404错误

官方推荐的解决方法是在服务端增加一个覆盖所有情况的候选资源:如果url匹配不到任何静态资源,则应该返回同一个index.html页面,这个页面就是你app依赖的页面。同时这么做以后,服务器就不再返回404错误页面,因为对于所有路径都会返回index.html文件。为了避免这种情况,在vue应用里面覆盖所有的路由情况,然后再给出一个404页面。或者如果是用node.js做后台,可以使用服务端的路由来匹配url,当没有匹配到路由的时候返回404,从而实现fallback