You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Plasmo的Chrome扩展:如何从Service Worker向Content Script发定时消息?

问题

我正在使用Plasmo开发Google Chrome扩展,需求是定期调用Content Script中的函数。尝试使用setInterval()但可靠性不足,了解到在Service Worker中使用chrome.alarms服务是更佳方案。

我的Service Worker中有如下代码:

// background/ports/timer.ts
import type { PlasmoMessaging } from "@plasmohq/messaging"

async function registerInitAlarm() {
  // Create an alarm that is invoked a second from now, for every 1.5 seconds
  await chrome.alarms.create("initialize", {
    when: Date.now(),
    periodInMinutes: 1
  })
}

const handler: PlasmoMessaging.PortHandler = async (req, res) => {
  const { type, message } = req.body

  if (type === "register") {
    await registerInitAlarm()
  }
}

chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === "initialize") {
    // How do I send a message here to the content script?
  }
})

export default handler

根据Plasmo文档,需使用Ports API,但该API基于请求/响应模式,不清楚在alarm触发时如何向Content Script发送消息。另外,若不使用Plasmo框架/API,常规Chrome扩展文档要求先指定标签页再发消息,但可能存在多个加载了扩展Content Script的标签页,该如何处理?


解决方案

一、使用Plasmo的实现方式

Plasmo的Ports API虽以请求/响应为主,但可通过持久化端口连接实现主动推送:

  1. 在Content Script中建立长连接
    在Content Script里主动和Service Worker建立端口连接,并监听消息:

    // content.ts
    import { Port } from "@plasmohq/messaging"
    
    const timerPort = new Port({ name: "timer" })
    
    // 监听来自Service Worker的触发消息
    timerPort.onMessage.addListener((message) => {
      if (message.type === "trigger-task") {
        // 调用你需要定期执行的Content Script函数
        runYourContentScriptFunction()
      }
    })
    
    // 页面卸载时断开连接
    window.addEventListener("beforeunload", () => {
      timerPort.disconnect()
    })
    
    function runYourContentScriptFunction() {
      console.log("定期执行的Content Script逻辑")
    }
    
  2. 在Service Worker中维护连接并触发消息
    修改Service Worker代码,保存所有活跃端口,alarm触发时遍历发送消息:

    // background/ports/timer.ts
    import type { PlasmoMessaging } from "@plasmohq/messaging"
    
    // 存储所有活跃的端口连接
    const activePorts = new Set<PlasmoMessaging.Port>()
    
    async function registerInitAlarm() {
      await chrome.alarms.create("initialize", {
        when: Date.now(),
        periodInMinutes: 1
      })
    }
    
    const handler: PlasmoMessaging.PortHandler = async (req, res) => {
      // 保存新建立的端口
      activePorts.add(req.port)
      // 端口断开时自动移除
      req.port.onDisconnect.addListener(() => {
        activePorts.delete(req.port)
      })
    
      if (req.body.type === "register") {
        await registerInitAlarm()
      }
    }
    
    chrome.alarms.onAlarm.addListener((alarm) => {
      if (alarm.name === "initialize") {
        // 遍历所有活跃端口,发送触发指令
        activePorts.forEach(port => {
          port.postMessage({ type: "trigger-task" })
        })
      }
    })
    
    export default handler
    

二、原生Chrome扩展实现方式(不依赖Plasmo)

直接使用Chrome原生API,通过查询目标标签页批量发送消息:

  1. Service Worker中批量发送消息
    修改alarm监听逻辑,查询所有匹配的标签页并逐个发送消息:

    // background.js
    async function registerInitAlarm() {
      await chrome.alarms.create("initialize", {
        when: Date.now(),
        periodInMinutes: 1
      })
    }
    
    chrome.alarms.onAlarm.addListener(async (alarm) => {
      if (alarm.name === "initialize") {
        // 查询所有注入了Content Script的标签页(URL规则需和manifest中一致)
        const targetTabs = await chrome.tabs.query({ url: ["http://*/*", "https://*/*"] })
        
        targetTabs.forEach(tab => {
          if (tab.id) {
            // 向标签页发送消息,忽略未注入脚本的标签页错误
            chrome.tabs.sendMessage(tab.id, { type: "trigger-task" })
              .catch(err => console.log(`标签页${tab.id}未注入脚本`, err))
          }
        })
      }
    })
    
    // 处理来自Content Script的注册请求
    chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
      if (req.type === "register") {
        registerInitAlarm()
        sendResponse({ status: "success" })
      }
    })
    
  2. Content Script中监听消息
    在Content Script里监听来自Service Worker的消息,触发目标函数:

    // content.js
    chrome.runtime.onMessage.addListener((message) => {
      if (message.type === "trigger-task") {
        runYourContentScriptFunction()
      }
    })
    
    function runYourContentScriptFunction() {
      console.log("定期执行的Content Script逻辑")
    }
    

关键注意事项

  • Plasmo方案中,持久端口会随标签页关闭自动断开,手动监听beforeunload可避免残留连接。
  • 原生方案中,chrome.tabs.query的URL规则必须和manifest.json中Content Script的注入规则完全匹配。
  • 两种方案都需要在权限声明中添加alarms(原生扩展还需tabs权限):
    "permissions": ["alarms", "tabs"]
    

内容的提问来源于stack exchange,提问作者callmekatootie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 03:35:24