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

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

标签页会话

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

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

标签页会话的生命周期

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

    如果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

csRyan的学习专栏
分享对于计算机科学的学习和思考,只发布有价值的文章: 对于那些网上已经有完整资料,且相关资料已经整...

So you're passionate? How passionate? What actions does your passion lead you to do? If the heart...

1.1k 声望
181 粉丝
0 条评论
推荐阅读
手写一个Parser - 代码简单而功能强大的Pratt Parsing
在编译的流程中,一个很重要的步骤是语法分析(又称解析,Parsing)。解析器(Parser)负责将Token流转化为抽象语法树(AST)。这篇文章介绍一种Parser的实现算法:Pratt Parsing,又称Top Down Operator Precede...

csRyan阅读 2.7k

手把手教你写一份优质的前端技术简历
不知不觉一年一度的秋招又来了,你收获了哪些大厂的面试邀约,又拿了多少offer呢?你身边是不是有挺多人技术比你差,但是却拿到了很多大厂的offer呢?其实,要想面试拿offer,首先要过得了简历那一关。如果一份简...

tonychen152阅读 17.7k评论 5

封面图
正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...

寒青56阅读 8.4k评论 11

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy48阅读 6.8k评论 12

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木75阅读 7k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs42阅读 6.8k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木45阅读 8.4k评论 6

So you're passionate? How passionate? What actions does your passion lead you to do? If the heart...

1.1k 声望
181 粉丝
宣传栏