history API

history对象表示当前窗口首次使用以来用户的导航历史记录,history对象不会暴露用户已经访问过的URL,但是会提供一些API来实现URL的前进和后退。

URL跳转

window.history 提供三个API来实现页面URL的跳转:
  • go():传递一个整数,正整数表示向前跳多少,负整数表示向后跳多少。
  • forward():向前跳一页,类似浏览器的前进按钮。
  • back(): 向后跳一页,类似浏览器的回退按钮。
例如:
window.history.back(); // 等同于 window.history.go(-1); window.history.forward(); // 等同于 window.history.go(1);
还可以通过查看长度属性的值来确定的历史堆栈中页面的数量:
window.history.length;
可以用这个属性来判断你的页面是否是用户第一个打开的页面:
if(window.history.length === 1){ // ... }

更新URL但不进行跳转

history API

history.pushState

随着前端技术的不断发展,传统的URL跳转API已经不能在满足我们的需求了,例如在SPA应用中,页面跳转本质上并非是向服务器请求新的页面,而是通过JavaScript、DOM API等技术直接由前端改变页面内容,这个时候就需要一种能够实现可以更新URL,但是不需要进行跳转的技术。
HTML5 引入了 history.pushState()和 history.replaceState()方法,它们分别可以添加和修改历史记录条目。他们和前面讲到的go、replace等API很相似,但是最大的区别是这两个方法只会改变URL,不会向服务器发送页面请求。Vue-Router的history模式底层就是使用的这种方法。
history.pushState()和 history.replaceState()方法接收三个参数:
  1. 状态对象:状态对象 state 是一个 JavaScript 对象,通过 pushState () 创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate 事件就会被触发,且该事件的 state 属性包含该历史记录条目状态对象的副本。状态对象的大小是有限制的,通常是500KB~1MB以内,如果超过这个限制就会报错。如果你需要更大的空间,建议使用 sessionStorage以及 localStorage
  1. 标题,目前没有实现,可以传个空字符串。
  1. URL:要跳转的URL,可以是相对路径URL,也可以是一个绝对路径URL,但是新 URL 必须与当前 URL 同源。
window.history.pushState({a: 1}, '', '/')
history.pushState()和 history.replaceState()方法的区别在于前者是新建一个历史记录,而后者是替换当前的历史记录。

window.onpopstate

每当活动的历史记录项发生变化时, popstate事件都会被传递给 window 对象。如果当前活动的历史记录项是被 pushState创建的,或者是由 replaceState改变的,那么 popstate事件的状态属性 state会包含一个当前历史记录状态对象的拷贝。
注意 调用 history.pushState()或者 history.replaceState()不会触发 popstate  事件。popstate事件只会在浏览器某些行为下触发,比如点击后退按钮(或者在JavaScript 中调用 history.back()  方法)。即,在同一文档的两个历史记录条目之间导航会触发该事件。

history.state

history.state可以获取到当前历史记录的状态对象,如果当前没有使用过pushState或者replaceState,则该属性返回null。

刷新

使用history.pushStatehistory.replaceState 时需要格外注意,当用户点击刷新按钮时页面重新加载,此时浏览器仍然会发送当前URL的http请求到服务器,因此使用这两个API时需要考虑到服务端对这些GET 请求的处理,你可以
  • 在服务端为每个URL设置一个真实的URL地址,返回对应的页面。
  • 将所有URL都指向index.html文件(SPA)
如果你用过vue-router的history模式,那么你应该就有过这种体验,如果你没有配置好服务端,那么当你在某个URL刷新时,会得到一个404的页面,而解决的方法是在服务端为该应用的所有路径都配置返回index.html文件,这背后的原理就是其底层使用的正是history.pushState

示例

下面示例中实现了三个按钮,其中pushState每点击一次都会执行一次history.pushState({a: a}),变量a初始等于0,每次使用都会+1,点击replaceState会执行history.replaceState({a: a}) ,点击back按钮时会执行history.back()函数以实现历史记录的回退,这会触发popState事件,此时你应当可以在控制台看到事件对象(event)。
 

location.hash

另一种能更新URL但不会进行跳转的方法是使用window.location.hashwindow.location 对象保存了当前URL中的各项信息,例如hostname、protocol、port和hash等属性,其中hash属性中保存了当前URL以’#’开头的URL片段,并且更新这部分片段浏览器不会发送新的http请求。
location.hash的一个应用在于页内跳转锚点,例如
<a href="#a">跳转到id=a的元素</a>
当点击该链接时,页面会跳转到对应id的元素上,但是不会发送请求重新加载页面。
window.location.hash 属性可以被修改,并且修改操作会将修改后的URL添加到历史记录中,以便能够实现前进和后退。
window.location.hash = '/s/1'
和history API类似,location.hash也具有相应的事件监听器:hashchange。
window.addEventListener('hashchange',function(e){console.log(e ) })
要注意的是,和前面的popstate事件一样,当修改window.location.hash时并不会触发hashchange事件,只有当前进或者后退,即在两个历史记录间切换时才会触发hashchange事件。

location.hash和history.push的区别

从某种程度来说,调用 pushState() 和 window.location = "#foo"基本上一样,他们都会在当前的 document 中创建和激活一个新的历史记录。但是 pushState() 有以下优势:
  • 新的 URL 可以是任何和当前 URL 同源的 URL。但是设置 window.location 只会在你只设置锚的时候才会使当前的 URL。
  • 非强制修改 URL。相反,设置 window.location = "#foo"; 仅仅会在锚的值不是 #foo 情况下创建一条新的历史记录。
  • 可以在新的历史记录中关联任何数据。window.location = "#foo"形式的操作,你只可以将所需数据写入锚的字符串中。