Firefox 扩展并非“每开一个标签页就重复启动一次”,而是按模块职责分层运行:背景脚本(background script)在浏览器启动或扩展启用时全局仅运行一次并长期驻留;内容脚本(content script)则按需、逐标签页独立注入执行,二者协同实现跨页面数据共享与自动化操作。
firefox 扩展并非“每开一个标签页就重复启动一次”,而是按模块职责分层运行:背景脚本(background script)在浏览器启动或扩展启用时**全局仅运行一次并长期驻留**;内容脚本(content script)则**按需、逐标签页独立注入执行**,二者协同实现跨页面数据共享与自动化操作。
Firefox 扩展采用多进程、模块化架构,其运行时机和作用域由 manifest.json 中声明的脚本类型严格决定。理解这一机制,是实现“从一个页面提取信息、自动填充到另一个页面”等跨标签页功能的前提。
✅ 核心执行模型:三类脚本各司其职
| 脚本类型 | 运行时机与范围 | 生命周期 | 典型用途 |
|---|---|---|---|
| Background Script | 浏览器启动/扩展启用时加载,全局唯一实例,常驻内存 | 持续运行(除非被禁用或崩溃) | 管理状态、监听事件、中转消息、持久化存储 |
| Content Script | 每匹配一个网页(按 content_scripts 规则),在对应 tab 的渲染进程中独立注入一次 | 与页面同生共死(页面刷新/关闭即卸载) | 操作 DOM、读取页面数据、触发用户交互 |
| Popup / Options Page | 用户点击图标或打开设置页时加载,每次打开新建实例 | 页面关闭即销毁 | 提供 UI 界面、配置入口 |
? 关键提示:不存在“整个扩展在每个 tab 里运行一遍”的概念。内容脚本虽按 tab 注入,但它本身是轻量、隔离、无状态的;真正承载逻辑与状态的是 background script。
? 实现跨标签页数据传递:以“复制表单 → 自动填充”为例
假设您希望在电商商品页(Tab A)提取 SKU 和价格,然后在比价工具页(Tab B)自动填入搜索框——这正是典型的跨 tab 协作场景,需通过 background script 作为“中央枢纽”完成:
步骤 1:在 content script(Tab A)中捕获数据并发送
// content-script-a.js(注入商品页)
document.addEventListener('DOMContentLoaded', () => {
const sku = document.querySelector('[data-sku]')?.textContent || '';
const price = document.querySelector('.price')?.innerText || '';
if (sku && price) {
// 发送消息至 background
chrome.runtime.sendMessage({
type: 'SAVE_PRODUCT_DATA',
payload: { sku, price, url: window.location.href }
});
}
});
步骤 2:background script 持久化并广播
// background.js
let sharedData = null;
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === 'SAVE_PRODUCT_DATA') {
sharedData = request.payload;
console.log('✅ 已缓存产品数据:', sharedData);
// 可选:向所有已打开的 content script 广播更新
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
tabs.forEach(tab => {
chrome.tabs.sendMessage(tab.id, {
type: 'DATA_AVAILABLE',
data: sharedData
}).catch(() => {}); // 忽略未监听的 tab
});
});
}
if (request.type === 'REQUEST_PRODUCT_DATA') {
sendResponse({ data: sharedData });
}
});
步骤 3:在 content script(Tab B)中请求并填充
// content-script-b.js(注入比价页)
chrome.runtime.sendMessage({ type: 'REQUEST_PRODUCT_DATA' }, response => {
if (response?.data) {
const input = document.querySelector('#search-input');
if (input && !input.value) {
input.value = `${response.data.sku} ${response.data.price}`;
input.dispatchEvent(new Event('input', { bubbles: true }));
console.log('✅ 已自动填充搜索框');
}
}
});
⚠️ 注意事项与最佳实践
-
权限声明不可少:manifest.json 中需声明 "permissions": ["activeTab", "scripting"](Manifest V3)或 "tabs"(V2),并为 content script 配置匹配模式(如 "matches": ["
"] 或具体域名)。 - 避免全局变量污染:content script 无法直接访问页面 JS 变量(沙箱隔离),需用 window.eval()(谨慎)或 executeScript 注入。
-
状态持久化建议:若需长期保存(如重启后仍可用),改用 chrome.storage.local 替代内存变量:
// 存储 chrome.storage.local.set({ lastProduct: sharedData }); // 读取 chrome.storage.local.get('lastProduct', ({ lastProduct }) => { /* ... */ }); - 安全边界牢记:content script 无法直接访问 background 的 DOM 或变量,所有通信必须通过 chrome.runtime.sendMessage / onMessage 完成,确保清晰的接口契约。
掌握这一“background 长驻 + content script 按需注入 + 消息驱动通信”的范式,您就能可靠构建出标签页间无缝协作的高级扩展功能——无论是自动填表、跨站同步、还是智能剪贴板增强,皆以此为基础。











