History API
Last updated
Last updated
History API 操作浏览器的 session history,通过 History 对象。History 对象的引用可以通过 window 的只读属性 history
获取到,即 window.history
。
不同于 :
session history 是指加载当前页面的 tab 或 frame 访问过的页面
browser history 是指用户访问过的页面的时间记录
History 接口不继承任何方法,它的定义如下:
history.length
表示在 history stack 里的 pages 的数量
其初始值是 1,包括当前加载的页面
history.state
表示 history stack 栈顶的那个 state 对象
其初始值是 null
,除非开始调用了 pushState()
或 replaceState()
可以使用以下三个方法在 session history 中前进和后退:
history.back()
:后退
history.forward()
:前进
history.go(num)
:从 session history 里加载特定页面
位置相对于当前 page,正数表前进、负数表后退
浏览器兼容性:IE 要求是个字符串,而不是整数
执行这三个方法时,浏览器地址栏里的 URL 会变,同时也会触发 popstate
事件。如果越界了,这三个方法不会产生任何效果,也不会报错和抛异常。
这三个方法都是异步的,可以通过监听 popstate
事件来判断导航何时完成。
要操作浏览器 session history 栈,有两个方法:
pushState()
, add a history entry,添加一条 history 条目
replaceState()
, modify the history entry,修改 history 条目
修改当前的 history 条目,将其替换为传入的 state
对象和 URL
该方法适用于处理对用户行为的响应
参数 state
:是一个可序列化的 JavaScript 对象
state
对象序列化后的大小不能超过 16 MiB,因为 Firefox 会将 state
对象保存到用户的磁盘中,以便在用户重启浏览器后可以恢复它们
如果大小超了,方法会抛出异常
如果真的需要大的存储空间,则可以使用 sessionStorage
或 localStorage
参数 unused
:因为历史原因不能为空,通常会传一个空字符串
建议传个空字符串,以向后兼容
参数 url
:是新 history 条目的 URL
新 URL 必须和当前 URL 同源,否则方法会抛出异常
新 URL 可以是绝对的,也可以是相对的(相对当前 URL)
如果该参数未指定,默认是文档的当前 URL
浏览器只改变地址栏里的 URL,并不会 reload 页面
调用 pushState()
和 replaceState()
这两个方法,只会替换浏览器地址栏里的 URL,并不会触发 popstate
事件。
eg1. 页面初始化时调用 init()
eg2. 响应页面上按钮的 click 事件
pushState()
的位置需要注意的是,pushState()
是直接在 stack 当前位置的后面插入的。
如果当前位置正是栈顶,那么就直接入栈,此时 length 会增加 1
否则先清空当前位置(不含)上面的所有元素,然后再入栈,此时 length 的长度取决于当前位置
将这种入栈逻辑对应到【点击浏览器的前进+后退箭头】+【在相同页签打开当前网页中的超链接】,就会觉得还算符合应用场景。
来看个例子感受下。
在页面执行了 init()
方法之后,此时 session history stack 的长度是 3,信息如下:
2 {page: 'About Page'}
, /about
1 {page: 'Home Page'}
, /home
0 null
初始的 history 如下图蓝框中标出的,位置在 2-/about
。
执行了两次 history.back()
之后,位置在 0-null
,如下图绿框中标出的。
此时点击了 pushState
按钮,位置 1 的内容变成了 {page: 'Push Test'}
, /push-test
,而 history stack 的长度变成了 2。如下图红框中标出的。
其它入栈场景,可自行测试(比如连续入栈相同的、比如要入栈的和下一个是相同的),结论都如上所述——只和是否在栈顶有关,又或者是一律先清空当前位置(不含)上面的栈元素,再入栈新元素。
replaceState()
相比 pushState()
方法的逻辑,replaceState()
的逻辑就直观了很多:直接替换当前的。
popstate
事件以下两个条件必须同时满足:
触发时机:当用户在 session history 栈里来回导航时触发(即便连续的两条记录的内容是相同的)
当 session history 栈里连续的两条记录的内容相同时,来回切换也会触发 popstate
事件。比如先连续 push 三条,然后再点击浏览器的回退按钮,此时依然会触发 popstate
事件:
event.state
是个只读属性,是提供给 pushState()
或 replaceState()
方法的 state
参数的拷贝。
在浏览器可能触发 popstate
事件的情况下,遵循的步骤大约是:
如果新条目不包含现有的 Document,那么浏览器会先拉取内容并创建其 Document
,即发送 DOMContentLoaded
和 load
事件,同时也会进行下面的步骤
处理当前条目
如果当前条目的标题不是通过 History API 设置的,那么就将其设置为 document.title
属性返回的字符串
如果当前条目有持久的用户状态(persisted user state)要保存,那么在离开它之前,浏览器会把那些信息和当前条目一起存储,比如文档的滚动位置、表单 inputs 的值等其它类似数据
如果新条目和当前条目的 Document 对象不同,则 document
属性要指向新条目的
新条目 Document
里的所有表单控件,对于有 autocomplete
的会自动完成配置
如果新条目的文档已经完全加载并准备就绪(即 readyState
是 complete
)并且该文档还不可见,那么就将其变为可见,然后在 PageTransitionEvent
的 persisted
属性是 true
的文档中触发 pageshow
事件
把文档的 URL 设置成新条目的 URL
如果是在启用替换的情况下执行 history 遍历,那么就从 history 中删除目标条目之前的那条
如果新条目没有持久的用户状态,并且它的 URL 片段不为空,则文档将滚动到该片段
将当前条目设置为新条目
如果新条目有一起保存的序列化 state
信息,那么该信息会被反序列化为 History.state
,否则,state
为 null
发送 popstate
事件:如果 state
的值变了,那么就会发送 popstate
事件
恢复持久化用户状态信息
发送 hashchange
事件:如果原始条目和新条目共享同一个文档,但它们的 URL 中有不同的片段,则会发送 hashchange
事件
历史上表示页面的标题,目前所有的浏览器都忽略了该参数
触发操作:只能是点击浏览器上的前进/后退的按钮或是(stack 未越界)
而执行 pushState()
, replaceState()
方法,虽然它两也会改变 the active history 条目