HTML5的popstate、pushState、replaceState记录(Vue-Router实现)

HTML5的popstate、pushState、replaceState记录

你越是认真生活,你的生活就会越美好——弗兰克·劳埃德·莱特
《人生果实》经典语录

Vue-Router源码里路由切换跳转的实现原理是通过调用浏览器原生的 history 的 pushState 接口或者 replaceState 方法实现

在这里记录一下popstate、pushState、replaceState相关内容,加深印象

popstate

popstate-MDN

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

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

需要注意的是调用history.pushState()或history.replaceState()不会触发popstate事件

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

不同的浏览器在加载页面时处理popstate事件的形式存在差异。页面加载时Chrome和Safari通常会触发(emit )popstate事件,但Firefox则不会。

示例

打开http://example.com/example.html页面运行以下代码, 会按预期那样打出log提示。

window.addEventListener('popstate', (event) => {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
});
history.pushState({page: 1}, "title 1", "?page=1");
history.pushState({page: 2}, "title 2", "?page=2");
history.replaceState({page: 3}, "title 3", "?page=3");
history.back(); // Logs "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // Logs "location: http://example.com/example.html, state: null
history.go(2);  // Logs "location: http://example.com/example.html?page=3, state: {"page":3}

使用onpopstate事件处理程序属性的相同示例:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
history.pushState({page: 1}, "title 1", "?page=1");
history.pushState({page: 2}, "title 2", "?page=2");
history.replaceState({page: 3}, "title 3", "?page=3");
history.back(); // Logs "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // Logs "location: http://example.com/example.html, state: null
history.go(2);  // Logs "location: http://example.com/example.html?page=3, state: {"page":3}

注意,虽然原始的历史项( http://example.com/example.html所对应的)没有关联的状态对象(state object),但稍后调用history.back()激活该历史项时,仍会触发popstate事件。

hashchange

hash改变时会触发该事件

window.addEventListener('hashchange', function() {
  console.log('The hash has changed!')
}, false);
function locationHashChanged() {
  if (location.hash === '#cool-feature') {
    console.log("You're visiting a cool feature!");
  }
}

window.onhashchange = locationHashChanged;

history.pushState()

pushState - MDN
在 HTML 文档中,history.pushState() 方法向当前浏览器会话的历史堆栈中添加一个状态(state)。

语法

history.pushState(state, title[, url])

参数

  • state:

状态对象是一个JavaScript对象,它与pushState()创建的新历史记录条目相关联。

当用户导航到新状态时,都会触发popstate (en-US)事件,并且该事件的状态属性包含历史记录条目的状态对象的副本。

状态对象可以是任何可以序列化的对象。 因为Firefox将状态对象保存到用户的磁盘上,以便用户重新启动浏览器后可以将其还原,所以我们对状态对象的序列化表示施加了640k个字符的大小限制。 如果将序列化表示形式大于此状态的状态对象传递给pushState(),则该方法将引发异常。 如果您需要更多空间,建议您使用 sessionStorage或者localStorage。

  • title

当前大多数浏览器都忽略此参数,尽管将来可能会使用它。 在此处传递空字符串应该可以防止将来对方法的更改。 或者,您可以为要移动的状态传递简短的标题。

  • url(可选)

新历史记录条目的URL由此参数指定。

请注意,浏览器不会在调用pushState() 之后尝试加载此URL,但可能会稍后尝试加载URL,例如在用户重新启动浏览器之后。 新的URL不必是绝对的。 如果是相对的,则相对于当前URL进行解析。 新网址必须与当前网址相同 origin; 否则,pushState()将引发异常。 如果未指定此参数,则将其设置为文档的当前URL。

描述

从某种程度来说, 调用 pushState()window.location = "#foo"基本上一样, 他们都会在当前的document中创建和激活一个新的历史记录。但是 pushState() 有以下优势:

  • 新的URL可以是任何和当前URL同源的URL。但是设置 window.location 只会在你只设置锚的时候才会使当前的URL。
  • 非强制修改URL。相反,设置 window.location = “#foo”; 仅仅会在锚的值不是#foo情况下创建一条新的历史记录。
  • 可以在新的历史记录中关联任何数据。window.location = "#foo"形式的操作,你只可以将所需数据写入锚的字符串中。

注意: pushState() 不会造成 hashchange (en-US) 事件调用, 即使新的URL和之前的URL只是锚的数据不同。

示例

以下代码通过设置state, title和url创建一条新的历史记录。

const state = { 'page_id': 1, 'user_id': 5 }
const title = ''
const url = 'hello-world.html'

history.pushState(state, title, url)

history.replaceState()

replaceState - MDN

replaceState()方法使用state objects, title,和 URL 作为参数, 修改当前历史记录实体,如果你想更新当前的state对象或者当前历史实体的URL来响应用户的的动作的话这个方法将会非常有用。

语法

history.replaceState(stateObj, title[, url]);

参数

  • stateObj

状态对象是一个JavaScript对象,它与传递给 replaceState 方法的历史记录实体相关联.

  • title

大部分浏览器忽略这个参数, 将来可能有用. 在此处传递空字符串应该可以防止将来对方法的更改。或者,您可以为该状态传递简短标题

  • url 可选

历史记录实体的URL. 新的URL跟当前的URL必须是同源; 否则 replaceState 抛出一个异常.

例子

假设 http://mozilla.org/foo.html 执行下面的 JavaScript 代码:

var stateObj = { foo: "bar" };
history.pushState(stateObj, "", "bar.html");

此时页面地址变成了http://mozilla.org/bar.html 同时新增了一条历史记录

然后执行下面的 JavaScript 代码:

history.replaceState(stateObj, "", "bar2.html");

此时页面地址变成http://mozilla.org/bar2.html 但是没有新增一条历史记录 说明只是当前页面地址变了

URL栏显示 http://mozilla.org/bar2.html, 但是不会加载 bar2.html 页面,甚至不会检查bar2.html 是否存在

假设用户跳转到 http://www.microsoft.com, 然后点击返回按钮.
这时, URL 栏将会显示 http://mozilla.org/bar2.html 页面. 如果用户此时再点击返回按钮, URL栏将会显示 http://mozilla.org/foo.html 页面, 最终绕过了 bar.html 页面.

推荐阅读

Vue源码学习目录

Vue源码学习完整目录

Vue Router源码(四)路径切换

Vue Router(四)路径切换


谢谢你阅读到了最后~
期待你关注、收藏、评论、点赞~
让我们一起 变得更强

上一篇:正在奔向你的路途之中----面试题(四)


下一篇:超!超!超简单,Linux安装Docker