原文:抱歉找不到了
HTML5 的 History API 可以让开发者不刷新整个页面就可以修改网站的 URL。这在使用 JavaScript 加载一个页面的一部分的时候尤其有用,用 JavaScript 新加载的这一部分内容是明显不同的, 因而有必要使用一个新的 URL(原文: This is particularly useful for loading portions of a page with JavaScript, such that the content is significantly different and warrants a new URL.)。
这里有一个例子,我们假设一个人从一个网站的 Homepage 前往访问 Help 页面。我们使用 Ajax 加载 Help 页面的内容。那个用户随后又访问 Product 页面,而我们还是用 Ajax 加载并替换内容。然后,用户想分享这个页面(即 Product 页面)的 URL。通过 History API,我们本可以(we could have been)随着用户的访问而正确地修改页面的 URL,所以他们看见(或分享或保存)的 URL 是相关的并且正确的。
基础原理
要查看这个 API 的特征,只需要打开 the Developer Tools(即浏览器的开发者工具),在控制台输入(type into)history
。如果你选择的浏览器支持这个 API,那么我们将会看到许多隶属于这个对象的方法(a host of methods attached to this object):
History {length: 2, state: null, scrollRestoration: "auto"}
length:2
scrollRestoration:"auto"
state:null
__proto__:History
back:back()
constructor:History()
forward:forward()
go:go()
length:(...)
get length:()
pushState:pushState()
replaceState:replaceState()
scrollRestoration:(...)
get scrollRestoration:()
set scrollRestoration:()
state:(...)
get state:()
Symbol(Symbol.toStringTag):"History"
__proto__:Object
我们只对说明中的pushState
和replaceState
感兴趣。回到控制台,我们可以试验一下这些方法并看看当我们使用这些方法的时候 URL 发生什么变化。我们稍后将覆盖其他参数,但现在我们所要使用的是最后的这个参数:
history.replaceState(null, null, 'hello');
尽管没有请求资源,窗口也保持着同一个页面,但上面的replaceState
方法用/hello
作为地址栏中 URL 的结尾(原文:The replaceState method above switches out the URL in the address bar with '/hello' despite no assets being requested and the window remaining on the same page. )。这里还有一个问题。当点击浏览器上的返回按钮的时候,我们会发现我们并没有返回到这篇文章的 URL ,而是回到我们之前所停留的页面。这是因为replaceState
没有处理浏览器的历史,它只是简单地替换地址栏中当前的 URL。
要解决(fix)这个问题,我们需要使用pushState
方法来代替:
history.pushState(null, null, 'hello');
现在,如果我们点击浏览器的返回按钮,我们会发现它会像我们所希望的一样运行,因为pushState
已经修改了我们的历史(记录)以包含我们刚刚所传递进去的 URL。这很有趣,但如果我们做一些不那么直接的尝试并且假装当前的 URL 一直都不是“css-tricks.com”而是完完全全是另一个网站,之后会发生什么呢?
history.pushState(null, null, 'https://twitter.com/hello');
这会抛出一个异常,因为 URL 必须与当前的 URL 同源(has to be of the same origin as the current one),否则,我们可能冒着重大的安全漏洞风险(原文:we might risk major security flaws )并且是开发者能够欺骗人们让他们完全相信自己是在一个不同的网站(give developers the ability to fool people into believing they were on a different website altogether)。
回头看看被传入到这个函数中的其他参数,我们可以总结如下:
history.pushState([data], [title], [url]);
- 如果网页的状态改变了,例如:每当有人按下浏览器中的返回或前进按钮,我们将需要的数据就是第一个参数。需要注意的是:在 Firefox 中,这个数据被限制为 640k 个字符。
-
title
是第二个参数,他可以是一个字符串,但截至本文写作时间(2015年3月9日),所有浏览器都简单地忽略它。 - 最后的这个参数就是我们希望出现在地址栏的 URL。
简史(A Quick History)
这些 history API 最值得注意的是,它们不会重新加载页面。在过去,改变 URL 的唯一方法是window.locaton
,但它总是会重新加载页面。 除非,如果你所改变的只是hash
(就像 点击<a href="#target">link</a>
不会重新加载页面 一样)(原文:Except, if all you changed was the hash
)。
这与旧的 hashbang 方法相通,hashbang 方法可以改变 URL 而不刷新整个页面。总所周知,Twitter 曾经这样子做并因此而在很大程度上收到了批评(hash 不是一个真的资源地址)。
Twitter 放弃了使用这个方法,并且成为 history API 最早的支持者之一。