chrome 扩展实践

chrome 扩展实践

tada-zako

Chrome 扩展基础结构

Chrome 扩展通常包含以下几个核心文件:

  • manifest.json:扩展的配置文件,定义扩展的名称、版本、权限等信息,最重要的是用来控制扩展的行为(包括声明 service worker、popup 页面和热键之类的功能)。
  • background.js:后台脚本,处理扩展的核心逻辑和事件监听。负责管理扩展的生命周期、数据状态等,具有访问 Chrome API 的权限。
  • content.js:内容脚本,注入到网页中以与页面内容交互。可以操作 DOM,监听用户操作等,但是不具有访问 Chrome 扩展 API 的权限。
  • popup.html:扩展的弹出界面,用户点击扩展图标时显示的内容。
  • 资源文件:如图标、样式表等。

基本上,和普通的网页开发类似,都是使用 HTML、CSS 和 JavaScript 来构建界面和功能。但是,扩展有自己的生命周期和权限模型,需要通过 manifest.json 来声明。

manifest.json 示例

manifest.json 中有几个必须要设置的字段:

  • manifest_version:指定清单文件的版本,目前最新的是 3。
  • name:扩展的名称。
  • version:扩展的版本号。

如下是一个比较全面的 manifest.json 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
// 必需字段
"manifest_version": 3,
"name": "My Chrome Extension",
"version": "1.0",

// 扩展描述
"description": "A simple Chrome extension example.",

// 权限声明
"permissions": ["scripting", "activeTab"],

// 背景脚本配置
"background": {
"service_worker": "background.js"
},

// 弹出页面配置
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},

// 内容脚本配置
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
}

其中「权限声明字段」是用来声明扩展需要使用到的 Chrome API 权限的,比如上面的 scriptingactiveTab

  • scripting 允许扩展动态注入脚本到网页中。
  • activeTab 允许扩展访问当前活动标签页的内容,设置后就不会弹权限请求提示。之所以不弹出权限请求,是因为设置这个字段后,权限就只能在用户当前操作的标签页中使用,限制了权限的范围,提升了安全性。而如果不设置,当用户选择信任扩展时,扩展就会请求对所有标签页的访问权限,可能会引起用户的担忧。

除此之外,还需要注意的就是「背景脚本」和「内容脚本」的不能直接相互调用的问题。背景脚本和内容脚本运行在不同的环境中,不能直接访问对方的变量和函数。如果需要它们之间通信,可以使用 Chrome 提供的消息传递机制,也就是chrome.tabs.sendMessagechrome.runtime.sendMessage(前者是当前标签页内部的通信,后者则是全局的通信) 和 chrome.runtime.onMessage 进行消息传递。

其实也没什么好说的,chrome 扩展的开发和普通的前端开发差别不大,主要是多了一个 manifest.json 来配置扩展的行为和权限,以及需要注意脚本之间的通信方式而已。

PDF 阅读器黑夜模式

实现效果展示:
PDF 阅读器黑夜模式

技术细节说明

PDF 阅读器的黑夜模式实现不难,主要就是通过内容脚本改变页面的 DOM 样式来实现。具体来说,可以在 content.js 中监听 background.js 中的信号,当用户触发(点击扩展,或者快捷键)时,内容脚本通过 document.querySelector 获取到 PDF 阅读器的容器元素,然后修改它的 CSS 样式。

当然也有别的方法,例如直接在 background.js 中使用 chrome 提供的 CSS 注入 API chrome.scripting.insertCSS 来直接注入 CSS 样式到页面中,这样就不需要在 content.js 中操作 DOM 了。

方法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// content.js

// 直接处理 embed 标签的 PDF (Edge 等浏览器)
function processEmbedPDF() {
const embed = document.querySelector(
"embed[type='application/pdf']"
) as HTMLElement | null;

if (embed) {
console.log("Found embed PDF");
toggleDarkModeStyle(embed);
}
}

// 切换元素的黑夜模式样式
function toggleDarkModeStyle(element: HTMLElement | null) {
if (!element) return;

const darkModeFilter = "filter: invert(1) hue-rotate(180deg);";
const prevStyle = element.getAttribute("style") || "";

if (!prevStyle.includes(darkModeFilter)) {
// 添加黑夜模式样式
element.setAttribute("style", `${prevStyle}; ${darkModeFilter}`);
} else {
// 移除黑夜模式样式
element.setAttribute("style", prevStyle.replace(darkModeFilter, ""));
}
}

// 监听来自 background script 的消息
chrome.runtime.onMessage.addListener((message: any, _sender, sendResponse) => {
if (message.action === "toggleDarkMode") {
console.log("Toggling dark mode, state:", message.state);
processEmbedPDF();
sendResponse();
return true; // 表示异步响应
}
return false;
});

方法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
// background.js
// 注入或移除 CSS 文件
if (newState === "ON") {
await chrome.scripting.insertCSS({
files: ["pdf-dark-reader.css"],
target: { tabId },
});
} else {
await chrome.scripting.removeCSS({
files: ["pdf-dark-reader.css"],
target: { tabId },
});
}

当然,实践发现,上述的操作只能在 Edge 浏览器中生效,因为 Edge 浏览器使用了 embed 标签来渲染 PDF,而 Chrome 浏览器则是使用了 shadow DOM 来渲染 PDF 的,而对于 shadow DOM ,浏览器扩展访问和修改起来比较麻烦,这里就没有继续深入了。

  • Title: chrome 扩展实践
  • Author: tada-zako
  • Created at : 2025-11-23 00:00:00
  • Updated at : 2025-11-25 21:37:06
  • Link: https://blog.tada-zako.top/2025/技术/chrome_extension/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments