Chrome 插件在新开页生效

34 阅读3分钟

Chrome 插件要在当前页面生效后,对从该页面点击打开的“其他页面(新标签页或同标签页跳转)”也生效,通常取决于你的插件是如何注入脚本的。

这里有几种常见的场景和对应的解决方案(均基于目前主流的 Manifest V3):


方法一:通过 manifest.json 自动匹配匹配规则(最简单、最常用)

如果你的插件功能是始终需要在某些特定的网站上运行的,不需要用户手动点击插件图标触发,那么直接在 manifest.json 中配置 content_scripts 即可。

{
  "manifest_version": 3,
  "name": "My Extension",
  "version": "1.0",
  "content_scripts": [
    {
      "matches": [
        "*://*.example.com/*", 
        "https://www.google.com/*"
      ],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ]
}

原理:只要新打开的页面 URL 匹配 matches 里的规则,Chrome 就会自动将 content.js 注入到新页面中,无需额外写代码。如果想对所有网页生效,可以使用 "<all_urls>"


方法二:通过 Background 脚本监听新页面加载(适合用户手动开启/关闭的插件)

如果你的插件是:用户点击插件图标(Action)后才在当前页面生效。那么默认情况下,新开的标签页是不会生效的。你需要通过 background.js (Service Worker) 来记住插件的“开启状态”,并在新页面加载时动态注入脚本。

1. 修改 manifest.json 添加权限:

{
  "permissions": ["tabs", "scripting", "storage", "activeTab"],
  "host_permissions": ["<all_urls>"],
  "background": {
    "service_worker": "background.js"
  }
}

2. 编写 background.js 你需要记录当前插件是否处于“激活”状态。

// 监听插件图标点击事件
chrome.action.onClicked.addListener((tab) => {
  // 读取当前状态并切换
  chrome.storage.local.get(['isActive'], (result) => {
    let newState = !result.isActive;
    chrome.storage.local.set({ isActive: newState });
    
    // 如果开启,立刻对当前页面注入脚本
    if (newState) {
      injectScript(tab.id);
    }
  });
});

// 监听标签页更新(当点击链接在当前页跳转,或打开新标签页时会触发)
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  // 确保页面加载完成,并且有 URL
  if (changeInfo.status === 'complete' && tab.url) {
    chrome.storage.local.get(['isActive'], (result) => {
      // 如果插件处于全局开启状态,则自动注入
      if (result.isActive) {
        injectScript(tabId);
      }
    });
  }
});

// 注入脚本的公共方法
function injectScript(tabId) {
  chrome.scripting.executeScript({
    target: { tabId: tabId },
    files: ['content.js']
  }).catch(err => console.log("注入失败:", err));
}

方法三:只针对“从当前已激活页面衍生出的新页面”(精准继承)

如果你不希望插件全局开启,只希望“从激活了插件的页面 A” 点击链接打开的“页面 B” 也被激活。 你可以利用 chrome.tabs.onCreated 中的 openerTabId 属性。

background.js 中:

// 用一个 Set 来存储被激活了插件的 Tab ID
let activeTabs = new Set();

chrome.action.onClicked.addListener((tab) => {
  // 激活当前 Tab
  activeTabs.add(tab.id);
  injectScript(tab.id);
});

// 监听新标签页被创建
chrome.tabs.onCreated.addListener((tab) => {
  // 如果新标签页是从一个已经激活的 Tab 中打开的 (openerTabId)
  if (tab.openerTabId && activeTabs.has(tab.openerTabId)) {
    // 将新标签页也加入激活列表
    activeTabs.add(tab.id);
  }
});

// 监听标签页加载完成
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete' && activeTabs.has(tabId)) {
    injectScript(tabId);
  }
});

// 标签页关闭时清理内存
chrome.tabs.onRemoved.addListener((tabId) => {
  activeTabs.delete(tabId);
});

function injectScript(tabId) {
  chrome.scripting.executeScript({
    target: { tabId: tabId },
    files: ['content.js']
  });
}

方法四:应对单页面应用 (SPA) 如 Vue/React 网站

如果当前网站是单页面应用(如 Bilibili、YouTube 等),点击链接时页面并不会真正刷新,只会通过 HTML5 History API 改变 URL 并局部重新渲染 DOM。

这时上面通过 Background 监听 onUpdated 的方式可能不够及时,你需要在你的 content.js 内部进行监听:

content.js 中监听 URL 变化或 DOM 变化:

// 1. 监听 URL 变化 (拦截 History API)
let lastUrl = location.href; 
new MutationObserver(() => {
  const url = location.href;
  if (url !== lastUrl) {
    lastUrl = url;
    onUrlChange(); // URL发生改变时重新执行你的逻辑
  }
}).observe(document, {subtree: true, childList: true});

function onUrlChange() {
  console.log("页面路由发生改变,重新应用插件效果");
  // 延迟一小段时间等待DOM渲染
  setTimeout(() => {
    applyMyExtensionLogic();
  }, 500);
}

// 你的核心逻辑
function applyMyExtensionLogic() {
  // ...改变背景色、提取数据等
}

// 初始加载执行一次
applyMyExtensionLogic();

总结建议:

  1. 静态且始终生效:选 方法一 (manifest.json 配置 matches)。
  2. 开关控制,全平台生效:选 方法二 (Background + chrome.storage)。
  3. 血统继承 (父传子):选 方法三 (Background + openerTabId)。
  4. 同页面无刷新跳转失效:选 方法四 (Content script 内 MutationObserver)。