chrome扩展+个人纯自定义代码开源

98 阅读5分钟

扩展是什么

自定义 Chrome,让 Chrome 的功能更强大

Background Pages

(控制器)一个扩展肯定需要长时间运行的脚本对扩展进行管理.这个脚本就属于 Background Pages 的一部分. 不过由于 Background Pages 是一直运行的,对资源占用比较多.现在已经用 Event Pages 代替.Event Pages 是按需加载,不需要的时候不会激活运行. Event Pages 的 JS 可以使用 chrome 的所有 API.

官方文档(opens new window)

Content Scripts

Content Scripts 是运行在网页上的. 在manifest 上进行网址匹配.当是目标网址的时候就加载这个 JS(或者使用 "<all_urls>" ). 这个js是注入到目标网页上,所以可以获取目标网页的dom- 内容脚本是在网页环境中运行的文件。通过使用标准文档对象模型 (DOM),开发者能够读取浏览器所访问网页的详情、更改这些网页,并将信息传递给其父级扩展程序

官方文档(opens new window)

可使用的chrome api

Message Passing

这个是 chrome 的通讯机制,是非常重要的一个知识点.开发扩展的 content scripts 几乎都是需要和 background pages 进行通讯的. 因为 content scripts 可以直接操作 DOM,background pages 可以使用所有的 chromeAPI.所以这俩 JS 需要交互才有意义

官方文档(opens new window)

这里的通信有两大种:

代码操作型

静态注入

  "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/*

开源代码

github.com/nowdothat/m…

仓库汇总了个人常用扩展,包括 Tampermonkey 脚本和自定义扩展