第 10 课:Offscreen Document

Chrome 插件开发指南 · 实战课程 · 离屏文档与后台 TTS

作者 David更新于

本课配套开源 Demo 10-offscreen-document:「网页文字转语音播放器」——Service Worker 无 DOM,通过 Offscreen Document 使用 SpeechSynthesis 等 Web API。

第 10 课成品:TTS 语音助手 Popup

▲ 输入文字后交给 Offscreen Document 调用 Web Speech API 朗读

本文讲解原理与关键片段;完整 HTML/CSS/JS 及逐步操作见仓库 10-offscreen-document 目录内的 README。

为什么需要 Offscreen

Service Worker 没有 window / document,无法直接调用:

  • SpeechSynthesisUtterance(TTS)
  • HTMLCanvasElement
  • Audio、DOMParser

Offscreen 创建用户不可见的 HTML 文档,完整支持上述 API。

Offscreen 内几乎不能用 chrome.tabs、chrome.storage、chrome.action 等——仅 runtime.sendMessage / getURL 等少数 API。其余操作必须由 Service Worker 代理。

全局同时只能存在 一个 Offscreen Document。createDocument 前必须 chrome.offscreen.hasDocument()。

通信架构

Popup sendMessage('speak')
    → background.js
        → ensureOffscreenDocument()
        → sendMessage({ action: 'doSpeak', target: 'offscreen' })
    → offscreen.js → speechSynthesis.speak()
        → sendMessage('speechEnded')
    → background.js → closeDocument() + 清除 Badge

manifest.json

{
  "permissions": ["offscreen"],
  "background": { "service_worker": "background.js" },
  "action": { "default_popup": "popup.html" }
}

background.js 片段

async function ensureOffscreenDocument() {
  if (!(await chrome.offscreen.hasDocument())) {
    await chrome.offscreen.createDocument({
      url: chrome.runtime.getURL('offscreen.html'),
      reasons: [chrome.offscreen.Reason.AUDIO_PLAYBACK],
      justification: '使用 Web Speech API 进行 TTS 播放',
    });
  }
}

chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
  if (msg.action === 'speak') {
    handleSpeak(msg.text, sendResponse);
    return true;
  }
});

offscreen.js 片段

chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
  if (message.target !== 'offscreen') return;
  if (message.action === 'doSpeak') {
    const u = new SpeechSynthesisUtterance(message.text);
    u.lang = /[\u4e00-\u9fa5]/.test(message.text) ? 'zh-CN' : 'en-US';
    u.onend = () => chrome.runtime.sendMessage({ action: 'speechEnded' });
    speechSynthesis.speak(u);
    sendResponse({ success: true });
  }
});

获取源码

git clone https://github.com/davidapp/chrome-extension-demos.git
cd chrome-extension-demos/10-offscreen-document

也可 Download ZIP 解压后进入 10-offscreen-document 目录。

体验与调试

  1. 打开 chrome://extensions → 开发者模式 → 加载已解压的扩展程序 → 选择 10-offscreen-document 目录
  2. Popup 输入文字 → 朗读 / 停止
  3. 朗读期间 chrome://extensions 出现 检查视图:offscreen;播放结束 SW 关闭文档,链接消失

10 课能力总览

课核心能力源码
01MV3、Popup、CSP01-hello-popup
02Content Script、scripting02-content-script
03Service Worker、Badge、storage03-service-worker
04sendMessage、return true04-message-passing
05storage.local、onChanged05-storage-api
06options_ui、storage.sync06-options-page
07contextMenus、Toast 反馈07-context-menus
08Side Panel、tabs 权限08-side-panel
09DNR 静态/动态规则09-declarative-net-request
10Offscreen、后台 TTS10-offscreen-document

完整仓库:davidapp/chrome-extension-demos

课后练习

  1. Popup 增加语速滑块、lang 下拉,传给 Offscreen 设置 utterance.rate / utterance.lang
  2. 用 Offscreen Canvas 生成文字像素图,toDataURL 回 Popup 展示

延伸阅读

小结

Offscreen 补全 SW 的 DOM 能力缺口;职责分离:Popup 触发、SW 管生命周期、Offscreen 只跑 Web API。十课实战完结,可继续阅读专栏工程化章节。

← 上一课:第 9 课:DNR · 回到:专栏首页

本页目录