不少开发者误以为 Firefox 扩展会“每打开一个标签页就运行一次”,实际情况远非如此。扩展采用模块化分层架构:背景脚本在扩展启动时便常驻后台,内容脚本则按需注入到匹配的页面中。两者协同工作,才能实现跨标签页的数据共享与自动填充这类高级功能。
Firefox 扩展的运作原理比表面看起来要复杂得多——它采用模块化运行模型,其生命周期与浏览器进程深度绑定,并非简单地随标签页的开启或关闭而启动或停止。掌握这一机制,是实现跨页面信息传递(例如从 A 页面提取表单数据,再自动填充到 B 页面)的核心前提。
一、三类核心脚本的运行时机与作用
| 脚本类型 | 启动时机 | 生命周期 | 典型用途 |
|---|---|---|---|
| Background 脚本 | 扩展安装、启用或浏览器启动时立即加载 | 持续运行,直至扩展被禁用或浏览器关闭 | 管理全局状态、监听事件、中转消息、持久化存储 |
| Content 脚本 | 按 manifest.json 配置或后台动态注入,在匹配页面加载时执行 | 与对应标签页寿命一致(页面刷新或跳转即重载) | 操作 DOM、读取页面数据、注入 UI 元素 |
| Popup / Options 脚本 | 用户点击图标或打开设置页时加载 | 页面关闭即卸载 | 提供用户交互界面 |
✅ 关键结论:Background 脚本是跨标签页通信的“中枢”——它始终在线,能够安全地保存数据,并协调多个 content script 的请求。
二、实现“从一页取数,填到另一页”的完整流程
接下来通过一个典型场景来展示工程化实现(以表单字段同步为例),看看实际代码是如何组织的。
1. 在 manifest.json 中声明权限与脚本
{
"manifest_version": 3,
"name": "Cross-Tab Autofill",
"version": "1.0",
"permissions": ["storage", "tabs"],
"host_permissions": [""],
"background": {
"service_worker": "background.js"
},
"content_scripts": [{
"matches": [""],
"js": ["content.js"],
"run_at": "document_idle"
}]
}
2. Background 脚本:持久化存储 + 消息路由
// background.js
let sharedData = {}; // 或使用 chrome.storage.local 更可靠
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "saveFormData") {
sharedData[sender.tab.id] = request.data;
console.log("✅ Saved form data from tab:", sender.tab.id);
sendResponse({ success: true });
} else if (request.action === "getFormData") {
// 返回所有已存数据(或按条件过滤)
sendResponse({ data: sharedData });
}
});
3. Content 脚本:页面侧数据采集与应用
// content.js
// 从当前页面提取表单字段(示例)
function extractForm() {
const inputs = document.querySelectorAll('input[type="text"], input[type="email"], textarea');
const data = {};
inputs.forEach(el => {
if (el.name && el.value.trim()) {
data[el.name] = el.value;
}
});
return data;
}
// 监听用户操作(如点击"保存到填充库"按钮)
document.addEventListener('click', e => {
if (e.target?.dataset?.saveForm) {
chrome.runtime.sendMessage({
action: "saveFormData",
data: extractForm()
});
}
});
// 自动填充:当检测到目标表单时触发
function autoFillTarget() {
chrome.runtime.sendMessage({ action: "getFormData" }, (response) => {
if (response?.data) {
Object.entries(response.data).forEach(([tabId, formData]) => {
Object.entries(formData).forEach(([name, value]) => {
const field = document.querySelector(`[name="${name}"]`);
if (field && !field.value) field.value = value;
});
});
}
});
}
// 可结合 MutationObserver 监听表单动态加载
if (document.querySelector('form')) autoFillTarget();
