浏览器的标签页会话机制,以及前进/后退缓存
对于浏览器来说,一个标签页就承载着一个标签页会话。
标签页会话
本文中所讲的session(会话)不是指客户端与服务端之间的会话:
- 客户端与服务端之间的会话是指:为了完成某个目标,客户端与服务端进行的一系列通讯(请求/响应对)。在这种会话中,服务端需要通过某种机制来识别出当前的请求属于哪一个会话(比如cookie)。
- 而本文所讲的标签页会话是指:在一个标签页的生命周期中,经历的一系列文档替换(卸载旧的文档,并加载新的文档)。文档替换过程中会发生的事件将总结在另一篇文章。
window.history
就封装了标签页内部的标签页会话模型。
标签页会话的生命周期
- 打开一个新的标签页,用户就开始了一个新的会话。(如果有多个标签页同时打开,说明用户同时处于多个会话当中)
当用户修改标签页的URL,或点击当前页面上的超链接(且
<a>
的target attribute为默认值_self
),或提交表单时,就会发生一次文档替换(卸载当前document,加载新的document)。标签页会向会话历史(session history)中增加一个会话历史条目(session history entry)。如果URL的修改只是造成hashchange(或者通过JavaScript修改了hash),也会增加一个会话历史条目,不过不需要替换文档了。
如果在JavaScript中调用了history.pushState(),也会增加一个会话历史条目,不过不需要替换文档了。类似地,history.replaceState()
会修改当前的会话历史条目,不替换文档。- 用户可以通过浏览器的前进/后退按钮在会话历史条目之间迁移。大部分浏览器支持用户在前进/后退按钮上点击鼠标右键,查看可以迁移到哪些会话历史条目。
- 有的浏览器(比如Firefox和Safari)还实现了Back-Forward Cache,从而能够更快地载入旧的会话历史条目。
- 通过
ctrl+shift+t
,用户能够恢复上一次关闭的标签页,以及它承载的会话历史。不过,Back-Forward Cache不会随着它的历史条目一起恢复,被恢复的历史条目的文档需要重新加载。
前进/后退缓存(Back-Forward Cache)
Firefox和Safari实现了Back-Forward Cache,在Webkit中它被称为Page Cache。关于它的详细信息可以查看参考资料1和2。我在这里想指出的是,Back-Forward Cache是与标签页会话机制深度结合的:
- 缓存时机:当标签页即将从一个会话历史条目迁移到另一个时,且需要文档替换时(前面举过一些不需要文档替换的例子),如果旧的文档满足某些条件(比如存在
unload
或beforeunload
的监听器,其他条件列举在Using Firefox 1.5 caching - Mozilla | MDN),那么旧的文档不会被销毁,而是保留在内存中并暂停其活动。 - 恢复时机:当用户通过浏览器的前进/后退按钮来载入旧的会话历史条目时,如果这个条目对应的文档有被缓存,那么直接从缓存中恢复这个文档并恢复其活动。
Back-Forward Cache的逻辑是:用户点击前进/后退按钮的时候,就是期待回到“之前看到过的那个页面”,所以浏览器不需要从服务器获取一份新的代码并重新加载页面。
关于Back-Forward Cache的讨论仅仅针对支持它的浏览器(Chrome不支持它)。
实验
<!DOCTYPE html> <!-- test2.html --> <html lang="en"> <head> <meta charset="UTF-8"> <title>Test</title> </head> <body> <a href="./test3.html">link</a> <script> console.log("loading"); // 这行只会在每次重新加载的时候打印 window.addEventListener("pageshow", function (event) { // 这行会在每次重新加载、从缓存中恢复的时候打印 console.log('pageshow', event.persisted, event); }); </script> </body> </html>
<!DOCTYPE html> <!-- test3.html --> <html lang="en"> <head> <meta charset="UTF-8"> <title>Test</title> </head> <body> <a href="./test2.html">link</a> <script> console.log("loading"); // 这行只会在每次重新加载的时候打印 window.addEventListener("pageshow", function (event) { // 这行会在每次重新加载、从缓存中恢复的时候打印 console.log('pageshow', event.persisted, event); }); </script> </body> </html>
步骤(使用Firefox):
- 先点击页面中的链接若干次,向标签页会话中增加历史条目。查看控制台,验证载入新文档的时候会输出"loading"和"pageshow"。
- 然后通过鼠标右键浏览器前进/后退按钮来查看前面/后面的会话历史条目。
- 选择旧的会话历史条目载入,查看控制台,验证载入新文档的时候只会输出"pageshow"而不会输出"loading",说明
<script>
并没有被重新执行,而是使用先前的DOM和JavaScript环境。 - 关闭标签页,再通过
ctrl+shift+t
恢复上次关闭的标签页,验证会话历史条目随着标签页一起被恢复了。加载先前的会话历史条目,发现文档没有从缓存中恢复而是重新加载,说明Back-Forward Cache在关闭标签页的时候被销毁了。
参考资料
- Using Firefox 1.5 caching - Mozilla | MDN
- WebKit Page Cache I – The Basics | WebKit
- Working with BFCache - Archive of obsolete content | MDN
- 7.1 Browsing contexts - HTML Standard 定义了标签页、browsing context、session history、Window、Document之间的关系
- 7.7 Session history and navigation - HTML Standard
相关推荐
ThinkMake 2020-11-13
学习web前端 2020-11-09
天空一样的蔚蓝 2020-10-23
curiousL 2020-08-03
sochrome 2020-07-29
SoarFly00 2020-06-28
LeoHan 2020-06-02
拭血 2020-06-02
lengyu0 2020-05-20
GimmeS 2020-05-15
逸璞丷昊 2020-03-08
lengyu0 2020-05-10
viewerlin 2020-05-10
DAV数据库 2020-05-07
程序员俱乐部 2020-05-06
Vampor 2020-05-01
ROES 2020-04-22
lcyangcss 2020-04-21