history 对象
history 对象表示当前窗口首次使用以来用户的导航历史记录。因为 history 是 window 的属性, 所以每个 window 都有自己的 history 对象。出于安全考虑,这个对象不会暴露用户访问过的 URL, 但可以通过它在不知道实际 URL 的情况下前进和后退。
history 对象提供了操纵浏览器历史记录的能力,开发者可以确 定历史记录中包含多少个条目,并以编程方式实现在历史记录中导航,而且也可以修改历史记录。
导航go()方法
go()方法可以在用户历史记录中沿任何方向导航,可以前进也可以后退。这个方法只接收一个参数, 这个参数可以是一个整数,表示前进或后退多少步。负值表示在历史记录中后退(类似点击浏览器的“后退”按钮),而正值表示在历史记录中前进(类似点击浏览器的“前进”按钮)。下面来看几个例子:
// 后退一页
history.go(-1);
// 前进一页
history.go(1);
// 前进两页
history.go(2);
在旧版本的一些浏览器中,go()方法的参数也可以是一个字符串,这种情况下浏览器会导航到历史中包含该字符串的第一个位置。最接近的位置可能涉及后退,也可能涉及前进。如果历史记录中没有匹配的项,则这个方法什么也不做,如下所示:
// 导航到最近的 wrox.com 页面
history.go("wrox.com");
// 导航到最近的 nczonline.net 页面
history.go("nczonline.net");
go()有两个简写方法:back() 和 forward()。顾名思义,这两个方法模拟了浏览器的后退按钮和前进按钮:
// 后退一页
history.back();
// 前进一页
history.forward();
history 对象还有一个 length 属性,表示历史记录中有多个条目。这个属性反映了历史记录的数量,包括可以前进和后退的页面。对于窗口或标签页中加载的第一个页面,history.length 等于 1。 通过以下方法测试这个值,可以确定用户浏览器的起点是不是你的页面:
if (history.length == 1){
// 这是用户窗口中的第一个页面
}
history 对象通常被用于创建“后退”和“前进”按钮,以及确定页面是不是用户历史记录中的第一条记录。
注意:如果页面 URL 发生变化,则会在历史记录中生成一个新条目。对于 2009 年以来发布的主流浏览器,这包括改变 URL 的散列值(因此,把 location.hash 设置为一个新值会在这些浏览器的历史记录中增加一条记录)。这个行为常被单页应用程序框架用来模拟前进和后退,这样做是为了不会因导航而触发页面刷新。
历史状态管理
现代 Web 应用程序开发中最难的环节之一就是历史记录管理。用户每次点击都会触发页面刷新的时代早已过去,“后退”和“前进”按钮对用户来说就代表“帮我切换一个状态”的历史也就随之结束了。为解决这个问题,首先出现的是 hashchange 事件。HTML5 也为 history 对象增加了方便的状态管理特性。
hashchange 会在页面 URL 的散列变化时被触发,开发者可以在此时执行某些操作。而状态管理 API 则可以让开发者改变浏览器 URL 而不会加载新页面。为此,可以使用 history.pushState()方法。这个方法接收 3 个参数:一个 state 对象、一个新状态的标题和一个(可选的)相对 URL。例如:
let stateObject = {foo:"bar"};
history.pushState(stateObject, "My title", "baz.html");
在HTML文档中,history.pushState() 方法向当前浏览器会话的历史堆栈中添加一个状态(state)。
语法
:
history.pushState(state, title[, url])
// state 状态对象是一个 JavaScript 对象,它与pushState()创建的新历史记录条目相关联。
// 状态对象可以是任何可以序列化的对象。
// title 可忽略,传递空字符串
// url 新历史记录条目的 URL 由此参数指定。
// 新的 URL 不必是绝对的。如果是相对的,则相对于当前 URL 进行解析。
// 新网址必须与当前网址相同 origin;否则,pushState()将引发异常。
// 如果未指定此参数,则将其设置为文档的当前 URL。
示例代码
:
const state = { page_id: "demo-panel", user_id: "demo-panel-user" };
const title = "";
const url = `/portal/log/logQuery?from=logPanel`;
history.pushState(state, title, url);
pushState() 方法执行后,状态信息就会被推到历史记录中,浏览器地址栏也会改变以反映新的相对 URL。除了这些变化之外,即使 location.href 返回的是地址栏中的内容,浏览器页不会向服务器发送请求。第二个参数并未被当前实现所使用,因此既可以传一个空字符串也可以传一个短标题。第一个参数应该包含正确初始化页面状态所必需的信息。为防止滥用,这个状态的对象大小是有限制的,通常在 500KB~1MB 以内。
因为 pushState()会创建新的历史记录,所以也会相应地启用“后退”按钮。此时单击“后退” 按钮,就会触发 window 对象上的 popstate 事件。popstate 事件的事件对象有一个 state 属性,其中包含通过 pushState()第一个参数传入的 state 对象:
window.addEventListener("popstate", (event) => {
let state = event.state;
if (state) { // 第一个页面加载时状态是 null
processState(state);
}
});
基于这个状态,应该把页面重置为状态对象所表示的状态(因为浏览器不会自动为你做这些)。记住,页面初次加载时没有状态。因此点击“后退”按钮直到返回最初页面时,event.state 会为 null。
可以通过 history.state 获取当前的状态对象,也可以使用 **replaceState()**并传入与 pushState()同样的前两个参数来更新状态。更新状态不会创建新历史记录,只会覆盖当前状态:
history.replaceState({newFoo: "newBar"}, "New title");
传给 pushState()和 replaceState()的 state 对象应该只包含可以被序列化的信息。因此, DOM 元素之类并不适合放到状态对象里保存。
注意:使用 HTML5 状态管理时,要确保通过 pushState()创建的每个“假”URL 背后都对应着服务器上一个真实的物理 URL。否则,单击“刷新”按钮会导致 404 错误。所有单页应用程序(SPA,Single Page Application)框架都必须通过服务器或客户端的某些配置解决这个问题。