chrome.offscreen 按需创建的不可见扩展页面。
准备工作
在manifest.json 中申请权限:
manifest.json
pages/ 特殊文件夹来声明,Extension.js 会像处理其他 HTML 入口一样把它编译出来:
pages/offscreen.html
创建、复用、关闭
Chrome 每个扩展只允许存在一个 offscreen 文档,已经存在时再调用createDocument 会抛错。可靠的写法是一个先检查是否已有文档的 ensure 函数:
background.ts
creating 锁很关键:两个事件可能在文档还没创建好之前同时进入 ensureOffscreen,第二次 createDocument 调用就会抛错。
工作做完后记得关闭它,以释放内存:
选择一个 reason
reasons 数组告诉 Chrome 这个文档为何存在。常见取值:
有一个行为值得了解:使用
AUDIO_PLAYBACK 时,Chrome 会在音频停止播放约 30 秒后自动关闭该文档。其他 reason 下,文档会一直存在,直到你主动关闭它或扩展被卸载。
在后台访问剪贴板
写入剪贴板是使用 offscreen 文档最常见的理由。navigator.clipboard 挂在 window 上,而
Manifest V3 的 service worker 没有 window,所以写入必须发生在一个真实页面里。
申请权限并给出对应的 reason:
manifest.json
pages/offscreen.html
pages/offscreen.ts
background.ts
createDocument 传入 chrome.offscreen.Reason.CLIPBOARD。
document.execCommand("copy") 才是这里能用的调用。更新的
navigator.clipboard.writeText 需要一个获得焦点的文档,而 offscreen 文档永远不会获得焦点。
读取剪贴板需要 clipboardRead 权限,以及同一个文档。
内容脚本,或者用户自己打开的扩展页面,都不需要这一套。它们都有真实的 window,所以在一次用户手势之后,navigator.clipboard.writeText 就能工作。
Extension.js 会帮你打包这个页面
chrome.offscreen.createDocument 接收的是一个 URL 字符串,而不是 import。Extension.js
会在构建期读取这个字符串,把该 HTML 页面以及页面加载的一切都加入产物。哪怕只有 service worker
提到过这个页面,它同样会出现在 dist/ 里。
这个字符串必须是纯字面量才行。在运行时拼出来的 URL 对构建不可见,页面会被漏掉。
与 offscreen 文档通信
offscreen 文档使用标准的 runtime 消息机制。给消息加上范围标识,让其他界面可以直接忽略:pages/offscreen.ts
Firefox
Firefox 没有实现chrome.offscreen。它的 Manifest V3 后台是以事件页面形式运行的,本身就具备 DOM 访问能力,所以同样的 DOM 工作可以直接在 Firefox 的后台脚本里完成。可以使用浏览器专属的 manifest 字段,再加上能力检测(typeof chrome.offscreen !== "undefined")来分支处理。

