浏览器的标签页会话机制,以及前进/后退缓存

对于浏览器来讲,一个标签页就承载着一个标签页会话。html

标签页会话

本文中所讲的session(会话)不是指客户端与服务端之间的会话:web

  • 客户端与服务端之间的会话是指:为了完成某个目标,客户端与服务端进行的一系列通信(请求/响应对)。在这种会话中,服务端须要经过某种机制来识别出当前的请求属于哪个会话(好比cookie)。
  • 而本文所讲的标签页会话是指:在一个标签页的生命周期中,经历的一系列文档替换(卸载旧的文档,并加载新的文档)。文档替换过程当中会发生的事件将总结在另外一篇文章。
window.history就封装了标签页内部的标签页会话模型。

标签页会话的生命周期

  1. 打开一个新的标签页,用户就开始了一个新的会话。(若是有多个标签页同时打开,说明用户同时处于多个会话当中)
  2. 当用户修改标签页的URL,或点击当前页面上的超连接(且<a>target attribute为默认值_self),或提交表单时,就会发生一次文档替换(卸载当前document,加载新的document)。标签页会向会话历史(session history)中增长一个会话历史条目(session history entry)。windows

    若是URL的修改只是形成 hashchange(或者经过JavaScript修改了hash),也会增长一个会话历史条目,不过不须要替换文档了。
    若是在JavaScript中调用了 history.pushState(),也会增长一个会话历史条目,不过不须要替换文档了。相似地, history.replaceState()修改当前的会话历史条目,不替换文档。
  3. 用户能够经过浏览器的前进/后退按钮在会话历史条目之间迁移。大部分浏览器支持用户在前进/后退按钮上点击鼠标右键,查看能够迁移到哪些会话历史条目。
  4. 有的浏览器(好比Firefox和Safari)还实现了Back-Forward Cache,从而可以更快地载入旧的会话历史条目。
  5. 经过ctrl+shift+t,用户可以恢复上一次关闭的标签页,以及它承载的会话历史。不过,Back-Forward Cache不会随着它的历史条目一块儿恢复,被恢复的历史条目的文档须要从新加载。

前进/后退缓存(Back-Forward Cache)

Firefox和Safari实现了Back-Forward Cache,在Webkit中它被称为Page Cache。关于它的详细信息能够查看参考资料1和2。我在这里想指出的是,Back-Forward Cache是与标签页会话机制深度结合的:浏览器

  • 缓存时机:当标签页即将从一个会话历史条目迁移到另外一个时,且须要文档替换时(前面举过一些不须要文档替换的例子),若是旧的文档知足某些条件(好比存在unloadbeforeunload的监听器,其余条件列举在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):服务器

  1. 先点击页面中的连接若干次,向标签页会话中增长历史条目。查看控制台,验证载入新文档的时候会输出"loading"和"pageshow"。
  2. 而后经过鼠标右键浏览器前进/后退按钮来查看前面/后面的会话历史条目。
  3. 选择旧的会话历史条目载入,查看控制台,验证载入新文档的时候只会输出"pageshow"而不会输出"loading",说明<script>并无被从新执行,而是使用先前的DOM和JavaScript环境。
  4. 关闭标签页,再经过ctrl+shift+t恢复上次关闭的标签页,验证会话历史条目随着标签页一块儿被恢复了。加载先前的会话历史条目,发现文档没有从缓存中恢复而是从新加载,说明Back-Forward Cache在关闭标签页的时候被销毁了。

参考资料

  1. Using Firefox 1.5 caching - Mozilla | MDN
  2. WebKit Page Cache I – The Basics | WebKit
  3. Working with BFCache - Archive of obsolete content | MDN
  4. 7.1 Browsing contexts - HTML Standard 定义了标签页、browsing context、session history、Window、Document之间的关系
  5. 7.7 Session history and navigation - HTML Standard
相关文章
相关标签/搜索