Skip to content

运行上下文与通信

Chrome 扩展的页面逻辑分布在多个上下文中:popup、content script 和 service worker。它们互相隔离,应按职责拆分,而不是共享全局状态。

Popup 是点击工具栏图标后打开的扩展页面,适合承载短时操作和设置入口。它在关闭后会销毁,不应把需要长期保存的状态只放在内存中。

json
{
  "action": {
    "default_popup": "popup.html"
  }
}

Popup 可以通过 chrome.tabs 查询标签页,再调用 chrome.tabs.update()chrome.windows.update() 激活目标标签页和窗口。

Content script

内容脚本运行在匹配网页中,负责 DOM 查询和页面展示。例如为文章标题后插入阅读时长:

js
const article = document.querySelector('article')

if (article) {
  const wordCount = [...article.textContent.matchAll(/[^\s]+/g)].length
  const readingTime = Math.max(1, Math.round(wordCount / 200))
  const badge = document.createElement('p')
  badge.textContent = `预计阅读 ${readingTime} 分钟`
  article.querySelector('h1')?.insertAdjacentElement('afterend', badge)
}

它不应承担需要跨页面保存、网络调度或高权限 API 调用的职责。

Service worker

MV3 的后台逻辑由 service worker 承担,适合响应安装、工具栏点击、定时任务和消息等事件。它可能被浏览器随时停止并在新事件到来时重启,因此关键状态应存入 chrome.storage,不要只依赖模块变量。

json
{
  "background": {
    "service_worker": "service-worker.js",
    "type": "module"
  }
}
js
chrome.runtime.onInstalled.addListener(({ reason }) => {
  if (reason === 'install') {
    chrome.storage.local.set({ enabled: false })
  }
})

一次性消息

内容脚本、popup 和 service worker 不能直接访问彼此的变量,可用 chrome.runtime.sendMessage()chrome.runtime.onMessage 交换 JSON 可序列化数据。

js
// content.js
const { tip } = await chrome.runtime.sendMessage({ type: 'get-tip' })

// service-worker.js
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
  if (message.type === 'get-tip') {
    chrome.storage.local.get('tip').then(sendResponse)
    return true
  }
})

异步调用 sendResponse 时需要返回 true,以保持消息通道有效。长时间、双向的通信使用 chrome.runtime.connect()chrome.tabs.connect() 创建 Port

安全边界

内容脚本会接触不受信任的网页。service worker 收到的消息应校验消息类型与字段;来自页面、用户输入或网络响应的数据不要直接写入 innerHTML,避免把高权限能力暴露给网页。

参考:内容脚本消息通信

基于 MIT 许可发布