首页首页
前端
非前端
辅助
Github
前端
非前端
辅助
Github
  • chrome

    • 谷歌插件开发
    • 谷歌插件应用
    • 谷歌插件Demo
  • 工具

    • 辅助工具
    • AI工具
    • 新机环境

1、谷歌插件概述

插件开发文档,需墙
插件开发中文文档

一、简单介绍

谷歌插件是运行在Chrome浏览器中的小型软件程序,用于扩展和定制浏览器的功能。
它基于HTML、CSS和JavaScript技术开发,通过manifest.json配置文件定义插件的属性和权限。

  • 主要功能
    • 内容修改:可以修改网页内容,如添加按钮、移除广告、高亮文本等
    • 浏览器操作:管理标签页、书签、历史记录,拦截网络请求
    • 数据存储:在本地存储用户数据,实现个性化设置
    • 跨域通信:与不同域名的网站进行数据交互
    • 用户界面:在工具栏添加图标、创建弹出页面、右键菜单选项
    • 后台任务:定时执行任务、处理事件、发送通知
  • 典型应用场景
    • 广告拦截器(如AdBlock)
    • 密码管理器(如LastPass)
    • 网页翻译工具
    • 开发者工具增强
    • 社交媒体增强插件
    • 网页截图工具

二、项目目录结构

  • manifest.json(必须)
    • 插件的“配置文件”,定义了名称、版本、权限、需要加载哪些脚本等核心信息。它使用JSON格式明确告知Chrome插件的能力,例如需要申请哪些权限(如访问特定网站数据、使用存储等),以及指定内容脚本(content_scripts)注入到哪些网页(通过matches字段匹配)
  • background.js​ (或 service-worker.js)
    • 插件的“后台进程”,负责管理生命周期和监听浏览器事件(如标签页更新),即使弹出窗口关闭也能运行。在Manifest V3版本中,它被实现为一个Service Worker。特点是无界面、常驻事件驱动(不持续占用资源)。它常用来设置右键菜单(contextMenus)或管理跨标签页的数据
  • content.js​ (名称可自定义)
    • “内容脚本”,被注入到特定网页中,用于读取或修改该网页的DOM,实现与页面内容的交互。这些脚本运行在被访问网页的上下文中,可以读取和修改页面的HTML和CSS。但它们与页面自身的JavaScript是隔离的,不能直接访问页面中定义的变量或函数,双方需要通过特殊API进行通信
  • popup.html​ (名称可自定义)
    • 点击浏览器工具栏插件图标后弹出的界面UI,通常伴随有对应的popup.js和popup.css
  • options.html
    • 插件的“设置页面”,用户可以通过右键点击插件图标选择“选项”来打开此页面进行详细配置

三、和普通网页开发的区别

特性维度普通网页开发谷歌插件开发
运行环境与作用域​在浏览器标签页内运行,受同源策略严格限制,通常只能操作自身页面内容运行于浏览器的扩展上下文,可跨域访问多个网站,权限范围更广
核心配置文件通常没有强制要求的根配置文件。必须包含 manifest.json​ 文件,用于声明插件元数据、权限和核心组件
核心能力与权限能力有限,主要依赖标准Web API,无法直接调用浏览器底层功能通过Chrome API可操作标签页、书签、网络请求等,需在manifest.json中声明权限
安全策略相对宽松,可以执行内联JavaScript(如onclick属性)或使用eval()函数受严格的内容安全策略 (CSP)​ 约束,默认禁止内联脚本和eval()等不安全操作,资源加载受限
生命周期与资源加载页面打开时加载,关闭时销毁;资源通常从远程服务器加载组件有独立生命周期(如Service Worker事件驱动、Popup随点击打开);代码和资源通常从本地打包文件加载
  • 调试工具:两者都使用Chrome开发者工具,但调试入口不同。
    • 插件后台Service Worker:在扩展管理页面点击对应插件的“查看视图”下的“Service Worker”链接进行调试。
    • 插件弹出页(Popup):右键点击浏览器工具栏上的插件图标,选择“检查”来调试Popup界面。
    • 插件内容脚本(Content Scripts):在目标网页的开发者工具中,切换到“Sources”页签,在“Content scripts”栏下找到并调试

2、Manifest V3特性

V2在2023年的时候已经停用。

特性类别Manifest V2Manifest V3核心变化与影响
架构核心后台页面 (Background Page)服务工作者 (Service Worker)​从持久运行的页面变为事件驱动、可被浏览器终止的工作线程。
网络请求处理webRequestAPI (命令式)declarativeNetRequestAPI​ (声明式)扩展声明规则,浏览器负责执行,无需读取请求内容,更隐私安全
代码执行允许远程托管的代码禁止远程代码​所有逻辑必须打包在扩展内,提高安全性和可审查性。
API 风格​普遍依赖回调 (Callbacks)广泛支持 Promise​支持现代异步编程模式,如 async/await,代码更简洁。
权限管理权限混合声明权限细分​主机权限 (host_permissions) 与 API 权限 (permissions) 分离,声明更清晰。
界面操作分离的 browser_action和 page_action统一的 actionAPI​简化了扩展图标及其相关弹出页面 (popup) 的定义

⚙️ 深入核心特性

一、生命周期管理

  • Service Worker 是事件驱动的,在不处理事件时会被浏览器终止(通常闲置约30秒后)以节省资源 。这意味着不能依赖全局变量来持久化状态。
  • 所有需要跨事件保存的数据都必须使用 chrome.storageAPI 或 IndexedDB

二、环境差异

  • Service Worker 运行在一个独立的环境中,没有 DOM 访问权限,也不能使用 XMLHttpRequest,必须使用现代的 fetch()API 进行网络请求

三、声明式网络请求

  • declarativeNetRequestAPI 的工作方式是:扩展预定义一组规则(匹配条件和操作),浏览器直接应用这些规则,扩展本身不介入每个请求的实时处理 。这避免了扩展需要读取所有网络请求内容的需求,保护了用户隐私。

四、远程代码禁令与应对策略

  • 此规则禁止从远程服务器加载 JavaScript 或 Wasm 文件,也禁止执行动态字符串代码(如 eval())这确保了提交到 Chrome 应用商店的扩展行为是固定的,便于安全审查。
  • 如果业务逻辑需要更新,官方推荐的替代方案是远程配置。扩展可以定期从服务器获取 JSON 等格式的配置文件,然后由扩展包内预置的、安全的逻辑来解析和执行这些配置

五、处理长时间任务

  • 由于 Service Worker 会被终止,需要执行周期性任务时应使用 chrome.alarmsAPI。对于极少数需要 DOM 访问的后台任务,可考虑使用 chrome.offscreenAPI 创建离屏文档

3、manifest.json字段详解

  • 必填字段
    • manifest_version:清单版本号,目前主流为v2和v3版本,v3是当前推荐标准
    • name:插件名称,字符串类型,必填字段
    • version:插件版本号,字符串类型,必填字段
  • 基本信息字段
    • description:插件功能描述,字符串类型
    • icons:插件图标配置,包含不同尺寸的图标文件路径,建议提供16x16、32x32、48x48、128x128像素的图标
    • author:插件作者信息,可包含email等联系方式
  • 权限配置字段
    • permissions:控制扩展能访问哪些Chrome API:如storage、tabs、activeTab、notifications等
    "permissions": [
        "storage",           // 存储数据
        "tabs",              // 访问标签页
        "bookmarks",         // 书签操作
        "notifications",     // 桌面通知
        "activeTab",         // 当前标签页临时权限
        "scripting",         // 脚本注入
        "alarms"             // 定时任务
    ]
    
    • host_permissions:声明插件可访问的主机域名权限,使用URL匹配模式
        "host_permissions": [
        "https://example.com/*",           // 特定域名
        "https://*.google.com/*",          // 子域名通配符
        "https://*/api/*",                 // 路径匹配
        "http://localhost:*/*",            // 本地开发服务器
        "<all_urls>"                       // 所有网址(谨慎使用)
    ]
    
    • optional_permissions:运行时由用户授予的可选权限
    • optional_host_permissions:运行时由用户授予的可选主机权限
  • 核心功能字段
    • action:定义插件在浏览器工具栏中的行为,包括图标、标题和弹出页面
    • background:定义后台脚本或页面,用于处理全局事件、管理状态等
    • content_scripts:定义注入到匹配页面的脚本和样式表,可指定注入时机和匹配规则
    • web_accessible_resources:定义插件中可供网页或其他插件访问的资源文件
  • 其他常用字段
    • options_page:插件设置页面
    • commands:定义快捷键命令
    • content_security_policy:定义内容安全策略
    • default_locale:默认本地化设置
    • update_url:插件更新地址

4、Popup 和 Options 页面

特性Popup (弹出页面)Options Page (选项页面)
主要用途临时性交互,快速操作插件配置和设置,功能更复杂
触发方式用户点击浏览器工具栏中的插件图标用户右键点击插件图标选择“选项”,或在扩展管理页面点击“选项”
生命周期短暂,焦点离开页面即关闭持久,作为独立标签页或弹窗存在
配置方式在 manifest.json的 action字段中设置 default_popup在 manifest.json中使用 options_page或更现代的 options_ui
页面能力能力受限,适合简单交互完整的网页能力,可构建复杂界面
打开方式无法通过程序代码打开,只能用户点击可通过 chrome.runtime.openOptionsPage()编程方式打开

5、Background Scripts(Service Worker)

Manifest V3 的 Background Scripts 是运行在 Service Worker 环境中的。
Service Worker 是 Manifest V3 中替代后台页面的技术,它是一个在浏览器后台独立运行的 JavaScript 文件,可以拦截和处理网络请求、管理缓存、接收推送消息等。

关键点:无持久状态、事件驱动、随时可能终止

特性Manifest V2 BackgroundManifest V3 Service Worker
运行环境后台页面(有DOM)Service Worker(无DOM)
生命周期可持久运行事件驱动,可能被终止
文件配置页面或脚本数组单个Service Worker文件
调试方式​chrome://extensions中打开chrome://serviceworker-internals
  • ✅ Service Worker 应该处理:
    • 事件监听:tabs.onUpdated、downloads.onCreated
    • 定时任务:chrome.alarms
    • 消息中转:不同页面间的通信
    • 数据同步:本地↔服务器同步
    • 推送通知:chrome.notifications

生命周期

    1. Installing 状态
// 安装阶段 - 只发生一次
self.addEventListener('install', event => {
  console.log('Service Worker 安装中...');
  // 跳过等待,立即激活
  event.waitUntil(
    // 预缓存关键资源
    caches.open('v1').then(cache => {
      return cache.addAll([
        '/',
        '/styles.css',
        '/app.js'
      ]);
    }).then(() => {
      // 强制进入激活状态
      return self.skipWaiting();
    })
  );
});
    1. Installed/Waiting 状态
    • 已安装但未激活
    • 等待旧的 Service Worker 控制的所有客户端关闭
    • 可通过 skipWaiting()立即激活
    1. Activating 状态
// 激活阶段
self.addEventListener('activate', event => {
  console.log('Service Worker 激活中...');
  event.waitUntil(
    // 清理旧缓存
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== 'v1') {
            return caches.delete(cacheName);
          }
        })
      );
    }).then(() => {
      // 立即控制所有客户端
      return self.clients.claim();
    })
  );
});
    1. Active 状态
// 激活后,开始处理事件
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(response => {
      return response || fetch(event.request);
    })
  );
});
    1. Terminated 状态
    • 空闲时自动终止
    • Chrome 会在 30 秒空闲后终止
    • 下次事件触发时重新启动
    • 所有变量状态都会丢失

无持久化状态

❌ 错误理解:认为 Service Worker 可以像普通页面一样保持变量值

✅ 正确理解:Service Worker 每次启动时都是全新的运行环境,所有内存状态都会重置

// ❌ 错误示例:状态会丢失
let requestCount = 0; // 每次Service Worker重启都会归零

// ✅ 正确示例:使用持久化存储
async function incrementCounter() {
  const data = await chrome.storage.local.get(['requestCount']);
  const count = (data.requestCount || 0) + 1;
  await chrome.storage.local.set({ requestCount: count });
  return count;
}

// 使用示例
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
  if (request.type === 'TRACK_REQUEST') {
    const newCount = await incrementCounter(); // ✅ 正确的状态管理
    sendResponse({ count: newCount });
  }
});
  • 实际影响
    • 不能依赖全局变量:每次启动都是从零开始
    • 必须使用存储API:chrome.storage、IndexedDB、Cache API
    • 初始化成本:每次启动都需要重新加载数据

事件驱动

❌ 错误理解:认为 Service Worker 可以主动轮询或持续运行
✅ 正确理解:Service Worker 只能被动响应事件,不能主动执行代码

// ❌ 错误示例:主动轮询(不会有效)
setInterval(() => {
  checkForUpdates(); // Service Worker 终止后定时器就没了
}, 60000);

// ✅ 正确示例:使用事件监听
// 1. 监听消息事件
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.type === 'CHECK_UPDATES') {
    checkForUpdates();
  }
});

// 2. 使用chrome.alarms(chrome会唤醒Service Worker)
chrome.alarms.create('checkUpdates', {
  periodInMinutes: 30
});

chrome.alarms.onAlarm.addListener(alarm => {
  if (alarm.name === 'checkUpdates') {
    checkForUpdates();
  }
});

// 3. 监听浏览器事件
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.url) {
    handleUrlChange(tab.url);
  }
});
  • 事件类型
    • 扩展事件:onMessage, onConnect
    • 浏览器事件:tabs.onCreated, webRequest.onBeforeRequest
    • 定时事件:alarms.onAlarm
    • 网络事件:fetch(普通网页SW用)

随时可能终止

❌ 错误理解:认为 Service Worker 启动后会一直运行
✅ 正确理解:Chrome 会在 Service Worker 空闲约30秒后自动终止它

// ❌ 错误示例:假设Service Worker会持续运行
function startLongRunningProcess() {
  // 这个处理如果超过30秒,可能被中断
  processLargeDataset();
}

// ✅ 正确示例:分块处理 + 状态保存
async function processLargeDatasetSafely() {
  const data = await loadData();
  const chunkSize = 100;
  
  for (let i = 0; i < data.length; i += chunkSize) {
    const chunk = data.slice(i, i + chunkSize);
    
    // 处理当前块
    await processChunk(chunk);
    
    // 保存进度
    await chrome.storage.local.set({
      processingProgress: i + chunk.length,
      lastProcessed: Date.now()
    });
    
    // 如果这是最后一块,清理进度
    if (i + chunkSize >= data.length) {
      await chrome.storage.local.remove(['processingProgress']);
    }
  }
}

// 恢复中断的任务
async function resumeInterruptedTask() {
  const state = await chrome.storage.local.get(['processingProgress']);
  if (state.processingProgress) {
    console.log('恢复任务,从进度', state.processingProgress, '继续');
    await processLargeDatasetSafely();
  }
}
  • 终止时机
    • 30秒空闲:无事件处理时
    • 内存压力:系统内存不足时
    • 浏览器关闭:用户关闭浏览器时
    • 扩展更新:扩展更新时

启动时机

Service Worker 的启动是事件驱动的,只有在特定事件发生时才会启动

1. 扩展相关事件

// Service Worker 会在以下事件发生时启动:
// 1️⃣ 扩展安装/更新
chrome.runtime.onInstalled.addListener(details => {
  console.log('Service Worker 因扩展安装/更新而启动');
  // details.reason 可能是:
  // - 'install'     (首次安装)
  // - 'update'      (扩展更新)
  // - 'chrome_update' (浏览器更新)
  // - 'shared_module_update'
});

2. API 事件监听

// 2️⃣ 监听的事件被触发时
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  console.log('Service Worker 因标签页更新而启动');
});
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  console.log('Service Worker 因收到消息而启动');
  return true; // 保持通道开放
});
chrome.runtime.onConnect.addListener(port => {
  console.log('Service Worker 因建立连接而启动');
});

3. 定时器事件

// 3️⃣ 注册的定时器触发
chrome.alarms.onAlarm.addListener(alarm => {
  console.log('Service Worker 因定时器触发而启动');
});

// 创建定时器
chrome.alarms.create('dailyCheck', {
  periodInMinutes: 24 * 60  // 每天触发
});

4. 浏览器事件

// 4️⃣ 浏览器事件触发
chrome.webRequest.onBeforeRequest.addListener(
  details => {
    console.log('Service Worker 因网络请求而启动');
  },
  { urls: ["<all_urls>"] }
);

启动时间轴示例

// 用户一天的典型使用场景:
8:00 打开浏览器 → ❌ Service Worker 未启动
8:05 点击扩展图标 → ✅ 启动(chrome.runtime.onMessage)
8:10 访问新网页 → ✅ 启动(chrome.tabs.onUpdated)
8:30 定时任务 → ✅ 启动(chrome.alarms.onAlarm)
8:35 关闭浏览器 → ❌ Service Worker 终止
14:00 重新打开浏览器 → ❌ Service Worker 未启动
14:05 收到推送 → ✅ 启动(chrome.gcm.onMessage)

无法直接访问DOM

// ❌ 错误:无法访问
document.getElementById('element');

// ✅ 正确:通过消息传递
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.type === 'UPDATE_DOM') {
    // 发送消息给content script处理
    chrome.tabs.sendMessage(sender.tab.id, {
      action: 'updateElement',
      data: request.data
    });
  }
});

通过offscreen访问DOM

chrome.offscreenAPI 是 Manifest V3 中访问 DOM 的唯一合法途径,用于在 Service Worker 中执行需要 DOM 的操作。

一、什么情况下需要用?

  • Service Worker 不能访问 DOM,但某些功能需要 DOM:
    • ✅ 解析 HTML/XML
    • ✅ 使用 Canvas
    • ✅ 处理音频/视频
    • ✅ 操作 document 对象

二、基本使用

    1. 配置权限
{
  "manifest_version": 3,
  "permissions": [
    "offscreen"  // 必需权限
  ]
}
    1. 创建离屏文档
// background.js
await chrome.offscreen.createDocument({
  url: 'offscreen.html',      // 离屏页面
  reasons: ['DOM_PARSER'],    // 用途说明
  justification: '解析HTML'   // 详细说明
});
    1. 通信处理
// 发送任务到离屏文档
chrome.runtime.sendMessage({
  type: 'PARSE_HTML',
  html: '<p>内容</p>'
});

// 离屏文档接收处理
chrome.runtime.onMessage.addListener((request) => {
  if (request.type === 'PARSE_HTML') {
    const doc = new DOMParser().parseFromString(request.html, 'text/html');
    // 处理DOM...
  }
});

三、重要限制

    1. 只能创建1个离屏文档
    1. 30秒不活动自动关闭
    1. 用户不可见,不显示窗口
    1. 需要明确声明使用原因

通信方式

1. 一次性消息传递(最常用)

// 发送方
chrome.runtime.sendMessage(
  { 
    type: 'ACTION_TYPE',
    data: { key: 'value' }
  },
  (response) => {
    console.log('收到响应:', response);
  }
);

// 接收方(任何地方)
chrome.runtime.onMessage.addListener(
  (request, sender, sendResponse) => {
    if (request.type === 'ACTION_TYPE') {
      // 处理请求
      const result = processData(request.data);
      
      // 发送响应
      sendResponse({ success: true, data: result });
      
      // 异步响应时返回 true
      return true;
    }
  }
);

2. 长连接通信

// 建立连接
const port = chrome.runtime.connect({ name: 'my-port' });

// 发送消息
port.postMessage({ action: 'doSomething' });

// 接收消息
port.onMessage.addListener((msg) => {
  console.log('收到消息:', msg);
});

// 断开连接
port.onDisconnect.addListener(() => {
  console.log('连接已断开');
});

3. 通过Storage共享

// 所有页面监听存储变化
chrome.storage.onChanged.addListener((changes, areaName) => {
  if (areaName === 'sync' || areaName === 'local') {
    if (changes.userSettings) {
      // 用户设置更新了
      updateUI(changes.userSettings.newValue);
    }
  }
});
// Popup修改设置
chrome.storage.sync.set({ userSettings: newSettings });
// Background和Options会自动收到通知

定时任务管理

// 创建定时器
chrome.alarms.create('periodicTask', {
  periodInMinutes: 60,  // 每小时执行一次
  delayInMinutes: 1     // 1分钟后首次执行
});

// 监听定时器触发
chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'periodicTask') {
    console.log('执行定时任务');
    performPeriodicTask();
  }
});

// 一次性定时器
chrome.alarms.create('oneTimeTask', {
  when: Date.now() + 5000  // 5秒后执行
});

// 销毁特定的定时器
chrome.alarms.clear('callApi');
// 销毁所有定时器
chrome.alarms.clearAll();

6、content scripts

Content Scripts​ 是 Chrome 扩展的核心组件,允许将 JavaScript 和 CSS 代码注入到特定的网页中,与页面内容直接交互。

一、核心特性

    1. 运行环境
    • 运行在网页的上下文中,能够访问和操作 DOM
    • 但与网页的 JavaScript 环境隔离,有自己的独立作用域
    // 在 content script 中
    var pageVar = "这是 content script 的变量";
    // 网页的 JavaScript 无法直接访问这个变量
    
    // 网页中的变量
    // var pageVar = "这是网页的变量";
    // content script 也无法直接访问
    
    • 可以访问部分 Chrome API(通过扩展 API)

二、配置方式

1. 静态声明 (manifest.json)

{
  "content_scripts": [
    {
      "matches": ["https://*.example.com/*"],
      "css": ["styles.css"],
      "js": ["content.js"],
      "run_at": "document_idle", // 注入时机
      "match_about_blank": false,
      "all_frames": false
    }
  ]
}
  • 注入时机选项:
    • document_start:DOM 加载前,CSS 加载前
    • document_end:DOM 加载后,图片等资源可能还在加载
    • document_idle(默认):DOM 完全加载后,window.onload前

2. 动态注入 (通过 background.js)

// 在 background.js 或 popup.js 中
chrome.scripting.executeScript({
  target: { tabId: tab.id },
  files: ['content.js']
});

三、与页面通信

1. DOM 事件通信

// content script 发送消息
window.postMessage({
  type: "FROM_CONTENT_SCRIPT",
  data: "Hello from content script"
}, "*");

// 网页监听消息
window.addEventListener("message", (event) => {
  if (event.data.type === "FROM_CONTENT_SCRIPT") {
    console.log(event.data.data);
  }
});

2. 与扩展其他部分通信

// 与 background script 通信
chrome.runtime.sendMessage(
  { action: "logData", data: "some data" },
  response => console.log("Response:", response)
);

// 监听来自 background 的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "updateContent") {
    // 更新页面内容
    sendResponse({ status: "updated" });
  }
});

四、访问 Chrome API 的权限

  • Content scripts 可以访问有限的 Chrome API:
    • chrome.runtime(发送消息、获取扩展信息)
    • chrome.storage(存储数据)
    • chrome.i18n(国际化)
  • 不能访问:
    • chrome.tabs(部分权限)
    • chrome.windows -需要更高权限的 API

五、隔离突破

在 Content Scripts 中,world​ 定义了脚本执行的 JavaScript 隔离环境。Chrome 94+ 引入了两种 world:

  • 1、"ISOLATED"(默认):隔离世界
  • 2、"MAIN":主世界(网页的 JS 环境) 核心作用:共享网页的 JavaScript 环境
  • 使用 world: "MAIN"让你能够:
    • ✅ 直接访问网页的 JavaScript 对象和函数
    • ✅ 修改网页的全局变量
    • ✅ 与网页框架(React、Vue 等)直接交互
  • 但要小心:
    • ⚠️ 可能引起命名冲突
    • ⚠️ 降低扩展的安全性
    • ⚠️ 可能破坏网页功能

建议:只有在确实需要与网页 JavaScript 深度交互时才使用 "MAIN",并采取适当的防护措施。

六、与浏览器插件通信

Content Scripts 与插件其他部分(popup、background、options等)通过 消息传递​ 机制通信,主要有三种方式:

    1. chrome.runtime.sendMessage()​ - 一次性消息
    1. chrome.runtime.connect()​ - 长连接通信
    1. DOM 事件​ - 与网页脚本通信

1. 一次性消息传递(最常用)

Content Script → Background/Popup

// content-script.js
// 发送消息
chrome.runtime.sendMessage(
  {
    type: "USER_ACTION",
    data: { action: "click", element: "button" }
  },
  response => {
    // 可选的回调,处理响应
    console.log("收到响应:", response);
  }
);

Background 接收消息

// background.js
chrome.runtime.onMessage.addListener(
  (request, sender, sendResponse) => {
    console.log("收到来自 content script 的消息:", request);
    console.log("发送者信息:", sender.tab?.url);
    if (request.type === "USER_ACTION") {
      // 处理消息
      const result = processAction(request.data);
      // 发送响应
      sendResponse({ success: true, data: result });
    }
    // 返回 true 表示异步响应
    return true; 
  }
);

2. 长连接通信(持续对话)

建立连接

// content-script.js
// 建立连接
const port = chrome.runtime.connect({ 
  name: "content-script-connection" 
});
// 监听来自 background 的消息
port.onMessage.addListener(msg => {
  console.log("收到消息:", msg);
  if (msg.command === "UPDATE_UI") {
    updatePageUI(msg.data);
  }
});
// 发送消息
port.postMessage({ 
  action: "CONNECTION_READY",
  tabId: chrome.devtools.inspectedWindow.tabId 
});
// 断开连接时
port.onDisconnect.addListener(() => {
  console.log("连接已断开");
  cleanup();
});

Background 处理连接

// background.js
chrome.runtime.onConnect.addListener(port => {
  console.log("新的连接:", port.name);
  // 只处理来自 content script 的连接
  if (port.name === "content-script-connection") {
    // 监听消息
    port.onMessage.addListener(msg => {
      console.log("来自 content script:", msg);
      // 响应消息
      if (msg.action === "CONNECTION_READY") {
        port.postMessage({ 
          command: "SEND_DATA", 
          data: { config: "default" } 
        });
      }
    });
    // 主动发送消息
    setTimeout(() => {
      port.postMessage({ command: "PING" });
    }, 1000);
  }
});

3. 与 Popup 页面通信

Popup → Content Script(需要标签页ID)

// popup.js
// 获取当前标签页
chrome.tabs.query(
  { active: true, currentWindow: true },
  tabs => {
    if (tabs[0]) {
      // 发送消息到该标签页的 content script
      chrome.tabs.sendMessage(
        tabs[0].id,
        { 
          type: "FROM_POPUP", 
          data: popupData 
        },
        response => {
          console.log("Content script 响应:", response);
        }
      );
    }
  }
);

4. 存储共享通信

通过 chrome.storage 共享数据

// content-script.js - 存储数据
chrome.storage.local.set(
  { pageData: { title: document.title, url: location.href } },
  () => {
    console.log("数据已保存");
  }
);
// background.js - 读取数据
chrome.storage.local.get(["pageData"], result => {
  console.log("获取的数据:", result.pageData);
});

通信限制与注意事项

  • 生命周期:Content script 随页面卸载而销毁
  • 跨域限制:Content script 只能与自己的扩展通信
  • 性能考虑:避免频繁发送大量数据
  • 安全考虑:验证消息来源
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  // 验证发送者
  if (sender.id !== chrome.runtime.id) {
    return; // 忽略非本扩展的消息
  }
});

7、Storage API(local、sync、managed)

特性chrome.storage.localchrome.storage.syncchrome.storage.managed
数据位置本地计算机Chrome 账户同步由管理员推送
存储上限10 MB100 KB 或 8 KB/项无明确限制
同步范围仅本机跨设备(登录同一账户)企业域内所有设备
适用场景本地临时数据、大文件用户设置、书签、偏好企业策略、统一配置
权限要求"storage""storage"无特殊权限

8、Tabs API

Tabs API 用于管理浏览器标签页,是扩展与网页交互的基础。

查询、创建、更新

查询标签页

// 获取当前标签页
chrome.tabs.query({active: true, currentWindow: true}, tabs => {
  const currentTab = tabs[0];
});
// 获取所有标签页
chrome.tabs.query({}, tabs => {});

创建标签页

chrome.tabs.create({url: 'https://example.com'});

更新标签页

chrome.tabs.update(tabId, {active: true});

创建、更新、激活、关闭事件监听

// 标签页创建
chrome.tabs.onCreated.addListener(tab => {});
// 标签页更新(URL/状态变化)
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.url) {
    console.log('URL变化:', changeInfo.url);
  }
});
// 标签页激活
chrome.tabs.onActivated.addListener(info => {});
// 标签页关闭
chrome.tabs.onRemoved.addListener(tabId => {});

权限要求

{
  "permissions": ["tabs"]
}
  • 核心使用场景
    • 获取当前页面信息​ - 用于内容脚本注入
    • 创建/管理标签页​ - 构建浏览器工具
    • 监听页面变化​ - 实现自动化功能
    • 跨标签页通信​ - 协调多页面操作

9、Runtime API

  • Runtime API 是扩展的"神经系统",负责:
    • 扩展生命周期管理
    • 组件间通信
    • 运行时信息获取

四个关键用途

1. 消息通信

// 发送消息
chrome.runtime.sendMessage({type: 'action'}, response => {});
// 接收消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  sendResponse({result: 'ok'});
  return true; // 异步响应时必需
});

2. 获取运行时信息

// 扩展信息
const manifest = chrome.runtime.getManifest();
const id = chrome.runtime.id;
const url = chrome.runtime.getURL('page.html');

3. 生命周期事件

// 扩展安装/更新
chrome.runtime.onInstalled.addListener(details => {
  if (details.reason === 'install') {
    // 首次安装
  }
});

4. 打开页面

// 打开设置页
chrome.runtime.openOptionsPage();
// 获取所有打开的页面
const views = chrome.extension.getViews({type: 'popup'});

10、Permissions API

Permissions API 用于动态管理扩展权限,让用户在需要时才授予权限,而非安装时强制要求。

请求和检查权限

1. 请求权限

// 运行时请求权限
chrome.permissions.request({
  permissions: ['storage'],
  origins: ['https://api.example.com/*']
}, granted => {
  if (granted) {
    console.log('权限已授予');
  }
});

2. 检查权限

// 检查是否拥有权限
chrome.permissions.contains({
  permissions: ['tabs']
}, result => {
  console.log('是否有tabs权限:', result);
});

11、Declarative Content API

Declarative Content API 允许扩展在特定页面自动激活,无需注入 content scripts。
主要控制扩展图标何时在工具栏中可点击,并可以添加页面操作按钮。

1. 设置规则

// 在background.js中
chrome.declarativeContent.onPageChanged.addRules([{
  conditions: [
    new chrome.declarativeContent.PageStateMatcher({
      pageUrl: { hostEquals: 'example.com' }
    })
  ],
  actions: [
    new chrome.declarativeContent.ShowPageAction()  // MV2
    // 或
    new chrome.declarativeContent.ShowAction()      // MV3
  ]
}]);

2. 清除规则

chrome.declarativeContent.onPageChanged.removeRules();
  • 使用场景
    • 智能图标显示:只在相关网站显示扩展图标
    • 减少注入:避免在所有页面注入content scripts
    • 性能优化:非相关页面不激活扩展

12、通知(Notifications)

Notifications API 允许扩展显示系统级别的通知,即使用户不在浏览器中也能看到。

1. 创建通知

// 简单通知
chrome.notifications.create('notification1', {
  type: 'basic',
  iconUrl: 'icon.png',
  title: '提醒',
  message: '任务已完成',
  buttons: [{ title: '查看' }]
});

// 带进度条
chrome.notifications.create('progress', {
  type: 'progress',
  title: '下载中',
  message: '正在下载文件...',
  progress: 50  // 0-100
});

2. 通知类型

  • basic- 基本通知(图标+文字)
  • image- 带图片的通知
  • list- 列表通知(显示多个项目)
  • progress- 进度条通知

3. 事件处理

// 用户点击通知
chrome.notifications.onClicked.addListener(notificationId => {
  console.log('通知被点击:', notificationId);
});

// 用户点击按钮
chrome.notifications.onButtonClicked.addListener((notificationId, buttonIndex) => {
  console.log('按钮被点击:', buttonIndex);
});
  • 重要特性
    • 系统级通知:即使浏览器最小化也能显示
    • 多种样式:支持图标、图片、列表、进度条
    • 交互支持:可点击通知或按钮
    • 自动消失:可设置优先级和超时时间

13、右键菜单(Context Menus)

Context Menus API 允许扩展在浏览器的右键菜单中添加自定义项目。

1. 创建菜单

// 创建菜单项
chrome.contextMenus.create({
  id: 'my-menu',
  title: '我的操作',
  contexts: ['selection']  // 选中文本时显示
});

// 子菜单
chrome.contextMenus.create({
  id: 'parent',
  title: '父菜单',
  contexts: ['all']
});
chrome.contextMenus.create({
  id: 'child',
  parentId: 'parent',
  title: '子菜单',
  contexts: ['all']
});

2. 可用上下文

  • all- 所有情况
  • page- 页面空白处
  • selection- 选中文本
  • link- 链接
  • image- 图片
  • video/audio- 视频/音频
  • editable- 可编辑区域

3. 点击事件

chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === 'my-menu') {
    console.log('选中的文本:', info.selectionText);
  }
});
  • 核心要点
    • background中创建:菜单必须在background script中创建
    • 多种上下文:可针对不同场景显示不同菜单
    • 图标支持:可添加图标到菜单项
    • 动态更新:可创建后更新、删除菜单

14、错误监控与日志收集

插件特定错误

// 监听扩展错误
chrome.runtime.onError.addListener(error => {
  console.error('扩展错误:', error);
});

15、Plasmo框架详解

目前比较受欢迎的开发框架和模板:

框架/模板名称核心特点适用场景
Plasmo Framework功能强大的浏览器扩展SDK,无需操心配置希望获得开箱即用、现代化开发体验的项目
Chrome Extension (MV3) Boilerplate with React 18 + Webpack5基于React 18和Webpack 5的现代化样板熟悉React生态,需要模块化、热重载的开发
Create Chrome Extension (.crx)基于Vite 4,支持多种前端框架,敏捷热更追求极速构建,并希望灵活选择Vue、React、Svelte等框架

一、核心特性

    1. 现代化开发体验
    • Plasmo内置了React和TypeScript的顶级支持,提供实时重载功能,修改代码后立即生效,React热模块替换技术保证了流畅的开发体验。
    1. 声明式开发模式
    • 采用声明式开发,无需手动处理繁琐的配置文件。框架自动处理从代码编译到插件打包的完整流程,大幅提升开发效率。
    1. 跨浏览器兼容
    • 同一份代码可以轻松部署到Chrome、Firefox、Edge等多个浏览器平台,解决了传统插件开发中最大的痛点之一。
    1. 强大生态系统
    • 从消息传递到存储管理,从环境变量到远程代码集成,Plasmo提供了完整的功能模块。

二、消息通信机制

Plasmo提供了简洁高效的消息通信机制,核心在于useMessage和usePort两个React钩子:

// 后台服务
import { relayMessage } from "@plasmohq/messaging"
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.type === "GET_USER_DATA") {
    sendResponse({ user: "Plasmo User", timestamp: new Date().toISOString() })
  }
})

// 内容脚本
import { useMessage } from "@plasmohq/messaging/hook"
const ContentScript = () => {
  const { data } = useMessage(async (req, res) => {
    console.log("Received message:", req.body)
    res.send({ status: "received" })
  })
  return <div>Message data: {JSON.stringify(data)}</div>
}

// 弹出页面
import { usePort } from "@plasmohq/messaging/hook"
const Popup = () => {
  const { send, data } = usePort("popup-port")
  const handleClick = () => send({ action: "FETCH_DATA" })
  return (
    <div>
      <button onClick={handleClick}>获取数据</button>
      <div>响应数据: {JSON.stringify(data)}</div>
    </div>
  )
}

三、状态管理

Plasmo的持久化存储API简化了跨上下文(弹出页、内容脚本、后台)的状态共享:

// store.ts - 状态定义
import { persistentStore } from "@plasmohq/persistent"
interface UserState {
  theme: "light" | "dark"
  notifications: boolean
}
export const userStore = persistentStore<UserState>({
  theme: "light",
  notifications: true
})

// popup.tsx - 修改状态
import { userStore } from "./store"
const ThemeToggle = () => {
  const [theme, setTheme] = userStore.useState("theme")
  return (
    <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
      Toggle {theme} theme
    </button>
  )
}

// content-script.tsx - 读取状态
import { userStore } from "./store"
userStore.watch((state) => {
  document.documentElement.dataset.theme = state.theme
})

16、谷歌插件打包以及发布

打包CRX文件

方案1:

通过浏览器扩展管理页面打包(推荐):在Chrome浏览器地址栏输入 chrome://extensions/并回车,打开右上角的 “开发者模式”​ 开关,点击 “打包扩展程序”​ 按钮,选择你的插件根目录(包含manifest.json的文件夹),然后点击 “打包扩展程序”​ 即可。成功后会生成一个 .crx文件和一个 .pem私钥文件。请务必妥善保管 .pem私钥文件,它是该扩展程序的“身份证”,后续更新版本时必须使用同一私钥重新签名,否则无法正常更新。

方案2:

通过命令行工具打包(适合自动化):可以使用 Google 提供的脚本或 Node.js 工具(如 crx3)进行打包。基本命令格式如下

# 使用官方脚本示例
python crx_make.py --extension-dir=/path/to/your/extension --key-file=/path/to/your/key.pem --output-crx=output.crx

提高审核通过率的建议

  • 权限申请遵循最小化原则:在 manifest.json中只申请插件运行所必需的权限。对于每个申请的权限,都要在提交页面的“权限”部分提供清晰、诚恳的理由,说明为何需要此权限以及如何保护用户数据。
  • 避免使用过于宽泛的权限:尽量避免使用 <all_urls>这样的主机权限。如果插件并非需要在所有网站上运行,应将其替换为具体的域名列表,或使用 activeTab权限(仅在用户点击插件图标时临时获取当前标签页的权限)。
  • 准备高质量的商品详情页:这是说服用户安装的重要环节。需要准备至少一张尺寸为 1280x800 或 640x400​ 像素的清晰宣传截图或宣传视频。插件的文字描述(包括名称、简短描述和详细说明)应准确、易懂,突出核心功能和价值。如果插件会处理用户数据,必须提供一份可公开访问的隐私政策

插件版本更新

1. 后台自动更新

Chrome浏览器会定期检查插件更新

// background.js - 监听更新事件
chrome.runtime.onUpdateAvailable.addListener(details => {
  console.log('发现新版本:', details.version);
  // 立即应用更新
  chrome.runtime.reload();
});
// 监听更新安装完成
chrome.runtime.onInstalled.addListener(details => {
  if (details.reason === 'update') {
    console.log('插件已更新至版本:', details.previousVersion, '→', chrome.runtime.getManifest().version);
  }
});

2. 手动检查更新

// 在popup或options页面中
function checkForUpdates() {
  chrome.runtime.requestUpdateCheck((status, details) => {
    if (status === 'update_available') {
      console.log('发现更新:', details.version);
      // 提示用户重启浏览器或重新加载插件
    } else if (status === 'no_update') {
      console.log('当前已是最新版本');
    } else if (status === 'throttled') {
      console.log('更新检查过于频繁,请稍后再试');
    }
  });
}
最后更新: 2026/1/12 22:21
Next
谷歌插件应用