问题背景
The storage is cleared if the extension is disabled, reloaded or updated and when the browser restarts.
---引用自Chrome Extension开发者手册
在 Chrome Extension(下称Chrome扩展)中,不同于 chrome.storage.local 的持久性存储方式,chrome.storage.session 会在插件被禁用、重启、更新和浏览器重启时清除数据,也就是说,每次插件在回到初始状态时,chrome.storage.session 里面的数据就会被清除。
从使用场景出发,chrome.storage.session 这一清除数据的时机特性 为减少网络请求提供了便利。
比如,有这样一种场景:
当用户开始使用 Chrome扩展 时,有一些对更新时效并不敏感、但需要从服务端或其他存储系统通过网络请求获得的数据,一般会在获取后被存储在 Chrome扩展侧 以供用户在使用扩展期间使用,而这些数据的更新时机只需放在这个扩展下一次启动时就可以了。
这个需求场景一般产生于:需减少网络请求来减少云服务商侧的一些带宽、请求次数的成本,即,对于一些时效不高的数据没必要多花钱。所以从理论的整体来看,chrome.storage.session 的数据清除时机特性与该场景应该是十分契合的。
而,本文命题来源于:Chrome扩展 的V3版本中,扩展后台 Service Worker 有 休眠 的概念,所以当我在 Chrome 开发者手册中看到 “reloaded” 字样时,我的心里就一直在打鼓:这个 “reloaded” 是否包括了休眠后重启时的那个 reload 呢?
如果包括的话,那么 chrome.storage.session 存储并不适用于减少成本的需求场景。
因为,一般我们的技术方案会这样设计:当用户开始使用 Chrome扩展 时,在 Service Worker 层检测xxx数据是否存在于缓存介质中,如果不存在则通过网络请求获取并存储于缓存介质中。然而,在用户使用过程中,绝大部分用户都能命中“ Chrome 扩展30秒休眠”的时机,毕竟我们不会频繁的打开 TAB 页(一般情况下,浏览一个页面需要点时间,这点时间再快也得超过30秒)。也就是说,如果这数据只能被存储30秒,以及,在下一次休眠唤醒时又需要重新请求网络的话,那么这样的缓存就失去了意义。(如果真这样,我们就要另择方案:比如使用长效缓存+编写代码自行判断缓存时间策略就行,就是麻烦点&这个方案也不在本次的讨论范围内)
所以,如果 Chrome 开发者手册中说的 “reloaded” 包括了休眠后再重启的时机,那么 “减少网络请求的这一需求在 chrome.storage.session 的使用场景中”这一逻辑就不成立。
问题答案
经过实践,chrome.storage.session 的数据清除时机 “reloaded”不包括“休眠后的重启时机”。
实践出真知
自古“实践出真知”,于是我设计了以下流程来消除我的困惑。(本文忽略 Content 与 Service Worker 的通信、休眠后自动重建长链接的代码)
设计方案说明
1. Extension底层设计

本次课题点涉及到 Chrome扩展 的 Content 与 Service Worker 层,通信流程说明如下:
Content 层首次向 Service Worker 请求时,理论上无法获取到数据,所以走向 “获取数据&缓存数据” 分支;
“ 在 Service Worker 休眠&由 Content 自动发起长链接重建后,再次向 Service Worker 请求数据时,是否能从 chrome.storage.session 中获取到数据 ”是我们本次需要验证的课题;
2. 用户操作交互设计

本次课题点涉及的交互有 TAB 页面与 Service Worker 后台,交互流程设计如下:
重启测试扩展后,打开或刷新一网页并打开开发者工具;
重启测试扩展是为了清除 chrome.storage.session 中的数据,以便排除以往数据的干扰;
打开或刷新网页时,扩展的 content.js 代码会被注入到网页中并执行。执行时,首先向 Service Worker 发送“初始化数据”消息,而后在当前页面插入一个可点击按钮;
Service Worker 接收到消息之后,检测数据是否存在,如果不存在,则向 session 中存储数据;
在插件中打开 Service Worker 后台,看到数据存储成功后,关闭后台;
当 Service Worker 的后台被打开时,Service Worker 进入休眠期所需要的时间远远大于不打开后台时的时间(此条信息没有开发者手册可以参考,完全由日常观察得出);
关闭 Service Worker 后台后回到网页,静静等待插件休眠后自动重启;
本文中,自动重启的代码本文并未提供,可以自动使用 port.onDisconnect.addListener 监听函数来进行长链接重建;
长链接重建成功后输出日志,以让用户看到该重建消息后,手动点击按钮;
当网页的开发者工具中,输出了“长链接重建”消息后,点击按钮 & 向 Service Worker 请求获取数据;
Service Worker 休眠时会自动断开与 Content 层的长链接,断开时,Content 层的 port 会收到 disconnect 监听消息,而我们需要做的就是在收到“断开连接”消息时又重建一次长链接;
基于4.1,当我们看到“长链接重建”消息时,不仅代表 Service Worker 曾经进入过休眠,也能让当前页面在不刷新的情况下向 Service Worker 发送长链接消息了;
此时打开 Service Worker 的后台,就可以看到“此次,数据是从获取-缓存分支中获取到的,还是直接从缓存中获取到的”的结果;
部分代码展示
本次课题点的代码主要涉及 Service Worker 读取数据 及 Content 层二次触发获取数据 两块代码。
首先,是在 Service Worker 中编写缓存&数据获取代码:
其次,是 Content 层中的两块内容:
1. 页面初加载时,由 Content 主动向 Service Worker 请求获取数据。此时,理论上,走向的是“请求数据并向 chrome.storage.session 中塞入数据”分支;
2. 在 Content 层注入可点击的按钮,以用于扩展休眠重启后被点击&向 Service Worker 获取数据;
测试交互分步解说
看过代码后,应该对扩展中各个部分所负责的内容有了一点印象,接下来我们就着交互的分步截图与截图说明再加深一些印象。

上图展示了“Chrome 扩展初始化时,从 Service Worker 获取缓存数据是无法获取到的,转而走向了设置数据分支”。(该图出现在交互流程中的“刷新测试扩展后、刷新一个网页”时)

上图展示的时机是:不操作浏览器的情况下,预计等待30秒左右,在开发者工具中显示“Content 层与 Service Worker 层之间的长链接被重建”的情况。
长链接之所以被重建是因为:
Service Worker 在进入休眠期时,会自动断开所有与 Content 层的长链接,长链接断开时,Content 层会收到监听端口被断开的消息;
而我在 Content 层的执行代码中监听了断开消息,当断开时,重新建立了 Content 层与 Service Worker 层的之间的长链接。

上图展示的是:当 Content 层与 Service Worker 层之间的长链接被重建后,即 Service Worker 休眠后,再次从 Service Worker 的 chrome.storage.session 中获取到了数据;
由此,实践说明:Service Worker 休眠并重启后,chrome.storage.session 的数据并未被清除。
写在最后
至此,本课题点验证过程结束。
虽然这是 Chrome Extension 学习旅途中一个极小的点,但我始终相信“细节决定成败”。
又或许,读到这里的你并不知道这个课题点有什么用,没事的,现在的你只需记住这个标题、记住这个网址,有需要的时候再来细细研究就好。


















