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()方法接收三个参数:
- 状态对象:状态对象 state 是一个 JavaScript 对象,通过 pushState () 创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate 事件就会被触发,且该事件的 state 属性包含该历史记录条目状态对象的副本。状态对象的大小是有限制的,通常是500KB~1MB以内,如果超过这个限制就会报错。如果你需要更大的空间,建议使用
sessionStorage
以及localStorage
。
- 标题,目前没有实现,可以传个空字符串。
- 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.pushState
和history.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.hash
。window.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"
形式的操作,你只可以将所需数据写入锚的字符串中。