简介

History对象最初设计用来表示窗口的浏览历史,但是,出于隐私方面的原因,History对象不再允许脚本访问已经访问过的实际URL。虽然,我们不清楚历史URL,但是,我们可以通过History对象的内置属性方法进行跳转。

一、路由模式解析

对象属性

要讲vue-router的路由模式,首先要了解的一点就是路由是由多个URL组成的,使用不同的URL可以相应的导航到不同的位置。

length

该属性代表着浏览器历史列表中的URL数量。初始值为1,如果当前窗口先后访问了两个网址,那该属性的值变为2。

history.length          // 1
// 访问了一个新的URL
history.length          // 2

如果有进行过服务器开发或者对http协议有所了解就会知道,浏览器中对页面的访问是无状态的,所以我们在切换不同的页面时都会重新进行请求。而实际使用vue和vue-router开发就会明白,在切换页面时是没有重新进行请求的,使用起来就好像页面是有状态的,这是什么原因呢。

state

HTML5新增属性,返回一个表示历史堆栈顶部的状态的值,这是一种可以不必等待popstate
事件而查看状态的方式。

这其实是借助了浏览器的History
API来实现的,这样可以使得页面跳转而不刷新,页面的状态就被维持在浏览器中了。

scrollRestoration

允许Web应用程序在历史导航上显式地设置默认滚动恢复行为。此属性可以是自动的(auto)或者手动的(manual)。

vue-router中默认使用的是hash模式,也就是会出现如下的URL:,URL中带有#号

对象方法

我们可以用如下代码修改成history模式:

go方法

go方法是History对象三个方法中的核心方法,通过go方法可以完美替代其他的两个方法。该方法接收一个可选参数,这个参数可以是number也可以是URL

ps:经过多次试验,传入URL参数貌似没有作用,有待后续研究

当使用number参数时,页面会跳转到History的URL列表的相对位置。比如当传入参数为-1,则相当于点击浏览器后退按钮的效果;当传入参数为1时,相当于点击浏览器前进按钮;当不传参数或者传入参数为0时,页面会刷新。

window.history.go(-1)           // 后退
window.history.go(1)            // 前进
window.history.go(0)            // 刷新
window.history.go()             // 刷新

tips:当传入的number对应的位置没有URL,则该条语句会静默失败

import Vue from 'vue'import Router from 'vue-router'import Main from '@/components/Main'Vue.useexport default new Router({ mode: 'history', routes: [ { path: '/', component: Main } ]})

forward方法

forward方法可以加载历史列表中的下一个URL,类似于go(1),实现了点击浏览器前进按钮的效果。

window.history.forward()        // 前进
window.history.go(1)            // 前进

这样子URL中的#号就被去除了。

back方法

back方法可以加载历史列表中的上一个URL,类似于go(-1),实现了点击浏览器后退按钮的效果。

window.history.back()           // 后退
window.history.go(-1)           // 后退

tips:当URL队列中没有上一个URL时,back方法会失效;同理,当URL队列中没有下一个URL时,forward方法会失效。

Hash: 使用URL的hash值来作为路由。支持所有浏览器。

HTML5新增方法

HTML5为History对象添加了两个新方法,pushStatereplaceState方法,用来在浏览历史中添加和修改记录。

History: 以来HTML5 History API 和服务器配置。参考官网中HTML5 History模式

pushState方法

pushState方法接收三个参数:

  • state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象(即不需要传参),可以设为null
  • title:新页面的标题,但是大部分浏览器目前都忽略这个值,所以这里也可以设为null
  • url:新的网址,必须与当前页处在同一域,浏览器的地址栏将显示这个网址

比如当前的网址是localhost:63342/1.html,使用pushState方法在浏览记录中可以添加一条新的记录:

window.history.pushState({params: 'aaa'}, null, '2.html')

输入这行语句后,浏览器地址栏中的URL变为了localhost:63342/2.html,但是无论这是不是一个真实网址,它都不会跳转,这只是一条历史记录。此时,可以通过state取到状态。

此时,如果你前往下个地址后点击后退按钮,页面将返回到localhost:63342/2.html,此时,也可以通过state属性取到状态。

window.history.state            // {params: "aaa"}

tips:如果pushState的第三个参数是一个跨域网址,控制台会报错,这主要是因为安全问题,防止不法分子伪装URL

window.history.pushState(null, null, 'www.baidu.com')       // 报错

Abstract:
支持所有javascript运行模式。如果发现没有浏览器的API,路由会自动强制进入这个模式。

replaceState方法

该方法基本和pushState一致,但是不同的是,该方法会直接替换当前的历史记录。

二、两种模式的区别

路由插件的原理

传统的History用法是操纵浏览器进行前进或后退的跳转,用处不是很大。但是,HTML5新增的方法为其带来了脱胎换骨的变化。

众所周知,vue-router等一众路由插件实现的功能是更新页面的视图,但是却不重新请求页面,也就是说,其实,他们并没有实际进行了跳转,而是修改了页面的DOM并通过修改页面的URL来模拟跳转。

在HTML5之前,页面路由只有hash模式,而HTML5中History对象的新增方法,带来了另一种模式:history模式。

1、hash模式

hash模式

在HTML5之前,vue-router是通过修改URL的hash值(URL中#开始的字符串,不了解的同学可以看我的上一篇文章)来达到修改页面URL并生成历史记录,但却不会重新请求页面。所以,不使用vue-router中history模式的情况下,你会发现你的路径前总会有一个#

比如,你在vue-router中设置的路径是/b,在你的想象中,路径应该是http://localhost:8080/b,但是,现实很骨感,实际路径是:http://localhost:8080/#/b,原因就是因为没开启history模式的情况下,vue-router是通过hashchange事件来监听URL中hash的改变并通过修改hash来模拟路径的变化。

由于通过window.location.hash修改hash是会有历史记录产生的,所以,在SPA中,依然可以通过后退、前进按钮来控制路由的跳转。

hash模式最大的优点是兼容性强,可以兼容一众老式浏览器。而它最大的缺点是,页面URL中一直挂着一个难看的#,这一点连vue-router的官网也对其进行了吐槽。

有需求就有功能,所以,当HTML5发布后,又有了history模式。

hash模式背后的原理是onhashchange事件,可以在window对象上监听这个事件:

银河国际平台官方网站,history模式

看到这里,如果认真看了pushState方法的同学应该已经差不多明白了,vue-router的history模式就是通过HTML5中History对象的pushState方法进行模拟的。

当vue-router每次需要跳转页面时,页面DOM的修改方式和时机并没有改变,和hash模式一样。但是,修改URL的方式改变了。此时,有了pushState方法,可以不用修改丑陋的hash模拟而是直接在历史记录中添加一条新的URL。

那么,没有了hash,如何监听URL的改变呢?HTML5还提供了一个popstate事件,当用户点击前进、后退按钮,或者调用backforwardgo方法时触发,可以监听URL的改变。

在这里提一句,使用history模式,就连路由传值都有更好的方式——使用pushState的第一个参数进行传值,使用History的state属性进行取值。

当使用了history模式时,使用vue-router跳入/b时,此时的页面URL不是丑陋的http://localhost:8080/#/b,而是预料之中的http://localhost:8080/b

// hash模式
window.location.href            // http://localhost:8080/#/b

// history
window.location.href            // http://localhost:8080/b
window.onhashchange = function{ console.log(event.oldURL, event.newURL); let hash = location.hash.slice; document.body.style.color = hash;}

总结

虽然,history模式提供了完美的URL显示,但是,正所谓鱼和熊掌不可兼得,兼容性和美观也不可兼得。只有兼容了HTML5的浏览器(IE10+)才能使用history模式,不然,就老实的继续使用hash模式吧。

所以,使用何种模式,还是取决于软件的兼容性,如果不需要兼容低级浏览器,那就放心大胆的使用history模式吧!

上面的代码可以通过改变hash来改变页面字体颜色,虽然没什么用,但是一定程度上说明了原理。

更关键的一点是,因为hash发生变化的url都会被浏览器记录下来,从而你会发现浏览器的前进后退都可以用了,同时点击后退时,页面字体颜色也会发生变化。这样一来,尽管浏览器没有请求服务器,但是页面状态和url一一关联起来,后来人们给它起了一个霸气的名字叫前端路由,成为了单页应用标配。

我们写个简单的方法来测试一下

 Document 测试一下  window.onhashchange = function{ console.log(event.oldURL,event.newURL) let hash = location.hash.slice; document.body.style.color = hash; } 

并且通过浏览器的前进、后退页面均可以变化。

网易云音乐,百度网盘就采用了hash路由,看起来就是这个样子:

2、history路由

随着history
api的到来,前端路由开始进化了,前面的hashchange,你只能改变#后面的url片段,而history
api则给了前端完全的自由

history api可以分为两大部分:切换和修改

包括back、forwardgo三个方法,对应浏览器的前进,后退,跳转操作,有同学说了,浏览器只有前进和后退,没有跳转,嗯,在前进后退上长按鼠标,会出来所有当前窗口的历史记录,从而可以跳转:

history.go;//后退两次history.go;//前进两次history.back(); //后退hsitory.forward修改历史状态包括了pushState、replaceState两个方法,这两个方法接收三个参数:stateObj,title,urlhistory.pushState({color:'red'}, 'red', 'red')window.onpopstate = function{ console.log if(event.state && event.state.color === 'red'){ document.body.style.color = 'red'; }}history.back;

通过pushstate把页面的状态保存在state对象中,当页面的url再变回这个url时,可以通过event.state取到这个state对象,从而可以对页面状态进行还原,这里的页面状态就是页面字体颜色,其实滚动条的位置,阅读进度,组件的开关的这些页面状态都可以存储到state的里面。

通过history api,我们丢掉了丑陋的#,但是它也有个毛病:

不怕前进,不怕后退,就怕刷新,f5,,因为刷新是实实在在地去请求服务器的。

在hash模式下,前端路由修改的是#中的信息,而浏览器请求时是不带它玩的,所以没有问题。但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,会分分钟刷出一个404来。

popstate实现history路由拦截,监听页面返回事件

当活动历史记录条目更改时,将触发popstate事件。

1、如果被激活的历史记录条目是通过对 history.pushState()
的调用创建的,或者受到对 history.replaceState()
的调用的影响,popstate事件的state属性包含历史条目的状态对象的副本。

2、需要注意的是调用 history.pushState() 或 history.replaceState()
用来在浏览历史中添加或修改记录,不会触发popstate事件;

只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back

 Document 测试一下  if (window.history && window.history.pushState) { window.onpopstate = function { console.log("location: " + document.location + ", state: " + JSON.stringify; //window.history.go //window.history.back() }; //window.addEventListener("popstate", function { // window.location = 'http://www.baidu.com'; //}, false); !function() { var state = { title: "title", url: "#" }; window.history.pushState; }(); } 

刷新时不打印,刷新多次,再后退,每次都有,直到为null

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。