扩展是什么
自定义 Chrome,让 Chrome 的功能更强大
Background Pages
(控制器)一个扩展肯定需要长时间运行的脚本对扩展进行管理.这个脚本就属于 Background Pages 的一部分. 不过由于 Background Pages 是一直运行的,对资源占用比较多.现在已经用 Event Pages 代替.Event Pages 是按需加载,不需要的时候不会激活运行. Event Pages 的 JS 可以使用 chrome 的所有 API.
Content Scripts
Content Scripts 是运行在网页上的. 在manifest 上进行网址匹配.当是目标网址的时候就加载这个 JS(或者使用 "<all_urls>" ). 这个js是注入到目标网页上,所以可以获取目标网页的dom- 内容脚本是在网页环境中运行的文件。通过使用标准文档对象模型 (DOM),开发者能够读取浏览器所访问网页的详情、更改这些网页,并将信息传递给其父级扩展程序
可使用的chrome api
domi18nstorageruntime.connect()runtime.getManifest()runtime.getURL()runtime.idruntime.onConnectruntime.onMessageruntime.sendMessage()
Message Passing
这个是 chrome 的通讯机制,是非常重要的一个知识点.开发扩展的 content scripts 几乎都是需要和 background pages 进行通讯的. 因为 content scripts 可以直接操作 DOM,background pages 可以使用所有的 chromeAPI.所以这俩 JS 需要交互才有意义
这里的通信有两大种:
代码操作型
静态注入
"permissions": [
"activeTab"
],
"content_scripts": [
{
"matches": ["*://*.bilibili.com/*"],
"js": ["content.js"]
}
],
动态声明注入
chrome.scripting
.registerContentScripts([{
id: "session-script",
js: ["content.js"],
persistAcrossSessions: false,
matches: ["*://example.com/*"],
runAt: "document_start",
}])
.then(() => console.log("registration complete"))
.catch((err) => console.warn("unexpected error", err))
这两种的区别是->一个需要在json中提前声明权限,一种是在后期js中直接写入或运行
通知处理型
one-time requests
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, { speed: value });
});
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
getSpeeds(request.speed)
});
Long-lived connections
bg.js
// background.js
console.log("Service worker is running!");
const connections = {};
chrome.runtime.onConnect.addListener((port) => {
// 创建一个唯一的连接 ID
const extensionListener = (message, sender, sendResponse) => {
// 处理从 content script 接收到的消息
console.log("Message received from content script:", message);
// 在这里添加处理逻辑
// 向 content script 发送消息
port.postMessage({ response: "Message received by background script" });
};
// 添加监听器
port.onMessage.addListener(extensionListener);
// 当连接断开时,移除监听器
port.onDisconnect.addListener(() => {
console.log("Port disconnected");
// 移除监听器
port.onMessage.removeListener(extensionListener);
// 移除连接记录
const tabs = Object.keys(connections);
for (const tab of tabs) {
if (connections[tab] === port) {
delete connections[tab];
break;
}
}
});
});
content.js
// content.js
console.log("Content script is running!");
// 连接到 background script
const port = chrome.runtime.connect({ name: "content-script" });
// 向 background script 发送消息
port.postMessage({ contentScriptMessage: "Hello from content script!" });
// 添加监听器以接收 background script 的响应
port.onMessage.addListener((message) => {
console.log("Message received from background script:", message);
// 在这里添加处理来自后台脚本的消息的逻辑
});
注意我的json配置为
{
"manifest_version": 3,
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
}
网页 JS 和 content js 通讯
网页 JS 也有自己的局限性,例如无法获取到 http only 的 cookies.这时候可以通过 content js 的帮助. content js 获取到数据之后怎么发给网页 JS 呢? content js 使用 window.postMessage 进行发送数据:
window.postMessage(msg, '*');
网页 JS 通过监听事件进行捕捉数据:
window.addEventListener('message', receiveMessage, false);
function receiveMessage(event) {
if (event.origin == window.location.origin) {
console.log(event.data);
}
}
注意 content js 和网页 JS 不共享变量,所以不能通过全局变量的方式进行通讯.不过可以通过 DOM 进行通讯.
content js 使用网页 JS 的变量.
如果网页本身加载了很多组件例如 JQuery,自己想使用但是因为不共享变量导致无法使用.可以使用 append 的 方式把自己 content js 里面写的函数直接注入到网页的 DOM 中,因为 DOM 是共享的.这样 content js 写的函数 就变成网页的 JS 进行运行了~
content js 注入
var script = document.createElement('script');
script.id = 'baidu_script';
script.appendChild(document.createTextNode('(' + baidu + ')();'));
(document.body || document.head || document.documentElement).appendChild(
script
);
这里注入的是一个立即执行函数,append 到 DOM 上的时候就会立即执行,并且可以使用网页 JS 的变量.
css 注入
有时候修改 DOM 的话肯定需要更改样式啊,这时候用内联样式必然太没效率,不能重用.就需要添加 CSS.
var css = function() {
/*
input{
border: 1px solid #C6C6C6;
box-shadow: 0 0 3px #C6C6C6;
-webkit-box-shadow: 0 0 3px #C6C6C6;
}
*/
}
.toString()
.slice(15, -4);
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.textContent = css;
document.head.appendChild(style);
首先定义一个匿名函数赋值给 css 变量,然后里面写了 css 内容,由于是注释掉的,其实并不会被 JS 执行,最后 转换成字符串的时候是能读到 CSS 文本的,slice 是去掉前后的注释符.
manifest
匹配模式
<scheme>://<host>/<path>
特殊情况
-
""匹配以允许的方案开头的所有网址,包括有效格式下列出的任何格式。由于会影响所有主机,因此在 Chrome 应用商店中审核使用它的扩展程序可能需要更长时间。
-
"file:///"允许您的扩展程序在本地文件上运行。此模式要求用户手动授予访问权限。请注意,该情况需要三个斜杠,而不是两个。
-
本地主机网址和 IP 地址
如需在开发期间匹配任何 localhost 端口,请使用
http://localhost/*。对于 IP 地址,请在路径中指定地址并添加通配符,例如http://127.0.0.1/*。您还可以使用http://*:*/*来匹配 localhost、IP 地址和任何端口。 -
顶级网域匹配模式
Chrome 不支持顶级域名 (TLD) 的匹配格式。在各个 TLD 中指定您的匹配模式,例如
http://google.es/*和http://google.fr/*。
开源代码
仓库汇总了个人常用扩展,包括 Tampermonkey 脚本和自定义扩展