Chrome扩展开发指南

news2025/1/4 20:21:14

前言

Chrome 扩展(通常也叫插件)也是软件程序,使用 Web(HTML, CSS, and JavaScript)技术栈开发。允许用户自定义 Chrome 浏览体验。开发者可以通过增加特效或功能来优化体验。例如:效率工具、信息聚合等等。你可以在 Chrome 应用商店 查看各种各样的扩展。比如一些翻译扩展、JSON 格式化扩展等等都极大提高了我们开发或工作效率。本文旨在帮助大家了解 Chrome 扩展开发的基本概念、开发流程。并最终开发一个背景颜色提取的扩展来加深 Chrome 扩展的印象。本文基于 Manifest V3,大部分内容翻译自https://developer.chrome.com/docs/extensions/mv3/。最终我们要实现的扩展功能如下:

98c90306f2e5830a8d0b6026d4f99a59.gif

基本概念

Manifest

扩展的 manifest 是唯一必须的文件,且文件名必须是 manifest.json。manifest 必须放在扩展程序的根目录,它记录了重要的 metadata、资源定义、权限声明并标识了哪些文件运行在后台和页面上。

Service Worker

扩展的 Service Worker 用户处理和监听 浏览器 事件。比如跳转到一个新页面、书签移除、tab 关闭等。它可以使用所有的 Chrome API,但是不能直接和网页交互(和网页交互需要 Content Scripts)。

Content Scripts

Content Script 可以在网页上下文中执行 JavaScript。也可以读取和修改它们注入页面的 DOM。Content Script只能使用部分 Chrome API。其余的可以通过 Service Worker 间接访问。

Popup 和 Pages

扩展可以包含多种 HTML 文件,比如弹窗、页面选项等,这些页面都可以访问 Chrome API。

开发一个简单扩展

下面我们来开发一个 Hello Extensions 的简单扩展

创建 manifest.json 文件

创建一个新的目录,在目录下创建一个名为 manifest.json 的文件。

mkdir hello_extension
cd hello_extension
touch manifest.json

manifest.json 中加入以下代码:

{
  "manifest_version": 3,
  "name": "Hello Extensions",
  "description": "Base Level Extension",
  "version": "1.0",
  "action": {
    "default_popup": "hello.html",
    "default_icon": "hello_extensions.png"
  }
}

上面的 JSON 文件描述了扩展的功能和配置,比如 action 中描述了在 Chrome 中需要显示的扩展的图标以及点击图标后弹出的页面。可以在这里 下载 图标到你的目录下,然后把名字改为 manifest.json 中配置的 default_icon

创建 html 文件

上面的 action 中还配置了一个 default_popup,接下来我们创建一个名为 hello.html 的文件并加入如下代码。

<html>
  <body>
    <h1>Hello Extensions</h1>
  </body>
</html>

加载扩展

在开发模式下加载未打包的扩展。

  • 在 Chrome 浏览器地址栏中输入 chrome://extensions。也可以通过下图的两种方式打开。

3fe8eae40e38e5edb14b740297592713.pnga1aa543d6406465695deac54cdb993ef.png

  • 打开开发者模式

d4b82e1fbfd9c5497f04e60ec95c7718.png
image.png
  • 点击加载已解压的扩展程序

9e334e86b2a212b9cd9fb2120ef3888e.png选择我们的文件夹43858a3696f641488669864c2d2edc31.png完成之后即可在扩展页面显示出来9564deb1ca2ae7acfb003ffbd552167a.png

固定扩展

261ba3dd6c0b867895bd5f40a1d4a113.png点击固定,我们的扩展就可以在工具栏显示了。然后我们点击 icon,就可以弹出对应的页面了。2a076dc4c14ff58f0295cfb22abe03a7.png

重新加载扩展

我们回到代码,修改扩展的名字为 “Hello Extensions of the world!”

{
  "manifest_version": 3,
  "name": "Hello Extensions of the world!",
  ...
}

保存之后,回到扩展页面,点击刷新,可以看到,名字已经改变。72a56dc28107d94534eb6a2c83c4c8f5.png那么每次改动都需要重新加载扩展吗?可以参考下表。

扩展组件是否需要重新加载
The manifestYes
Service workerYes
Content ScriptsYes (plus the host page)
The popupNo
Options pageNo
Other extension HTML pagesNo

查看控制台日志以及错误

Console logs

在开发过程中,你可以通过浏览器控制台日志调试代码。在之前的代码中加入 <script src="popup.js"></script>

<html>
  <body>
    <h1>Hello Extensions</h1>
    <script src="popup.js"></script>
  </body>
</html>

创建一个名为 popup.js 的文件并加入如下代码:

console.log("This is a popup!")

接下来:

  1. 刷新扩展

  2. 打开popup

  3. 右键popup

  4. 选择 Inspect(检查)

719c513b73bd86f64a3296e98427434e.png
image.png
  1. 在开发者工具中切换到Console tab。

c3e12073221f2d325d613c9438e79ec6.png
image.png

Error logs

下面我们修改 popup.js,去掉一个引号

console.log("This is a popup!) // ❌ 错误代码

刷新之后,然后再点击打开扩展,可以看到错误按钮出现。20cc4fb8ecca0db7e5b6d9d9d0c28d1b.png点击错误按钮,可以查看具体错误信息。2197de4416e564902b423ea9e16da634.png

扩展工程结构

扩展工程结构可以有多种方式,但是 manifest.json 文件必须位于根目录,下面是一种结构实例。84cb5d0b359ed5520ed2be23cc211acf.png

Content Script

Content Script 运行在网页的上下文环境中,通过标准的 DOM,Content Script 可以读取浏览器访问页面的细节,也可以对其进行修改,还可以把这些信息传递给父扩展。

理解Content Script的能力

Content Script可以直接使用一部分 chrome API,比如:

  • i18n

  • storage

  • runtime:

    • connect

    • getManifest

    • getURL

    • id

    • onConnect

    • onMessage

    • sendMessage

使用其他的可以通过发送消息的方式实现。Content Script可以在将扩展里的文件声明为 Web Accessible Resources 后访问这些文件。 Content Script 运行在一个独立的环境里,它可以对其所在的 JavaScript 环境进行修改,而不与页面或其他扩展的 Content Script 发生冲突。

<html>
  <button id="mybutton">click me</button>
  <script>
    var greeting = "hello, ";
    var button = document.getElementById("mybutton");
    button.person_name = "Bob";
    button.addEventListener(
      "click",
      () => alert(greeting + button.person_name + "."),
      false
    );
  </script>
</html>

ba3234c89de0c488dfc17c73c19fb20c.png0c2d0da5fe7d501454b47a9b20095f1a.png

Content Script 注入

Content Script 注入有三种方式,分别是静态声明、动态声明、编程注入。 静态声明比较简单,只需要在 manifest.json 文件的 content_scripts进行配置即可。注意需要指定 matches 字段以指明需要再哪些页面运行 Content Script 脚本。

"content_scripts": [
   {
     "matches": ["https://*.google.com/*"],
     "css": ["styles.css"],
     "js": ["content-script.js"]
   }
 ],

动态声明主要使用场景是 matches 不明确时使用。编程注入是需要响应事件或一些特定场合使用,我们最终的例子使用的是静态声明。

Service Worker

Chrome 扩展是基于事件的程序,用于修改或增强 Chrome 浏览器的浏览体验。事件是由浏览器触发的,比如导航到一个新页面,删除一个书签或关闭一个标签(tab)。扩展通过 Service Worker 中的脚本监听这些事件。然后执行特定的指令。 Service Worker 需要时被加载,休眠时被卸载。比如:

  • 扩展第一次安装或更新到一个新的版本

  • 一个扩展事件被触发

  • Content Script 或其他扩展发送了消息

Service Worker 被加载后,只要有事件 Service Worker 就会保持在运行状态。一旦空闲了30秒,浏览器就会停止它。 Service Worker 保持休眠状态,直到有它监听的事件发生,这时它会执行相应的事件监听器,然后空闲、卸载。

Service Worker 的日志在这里查看19e56e2761b4c33bb2b04a3b02ebef86.png

注册Service Worker

"background": {
    "service_worker": "background.js"
  },

初始化扩展

chrome.runtime.onInstalled.addListener(function (details) {
  console.log("onInstalled event has been triggered with details: ", details);
  // 检查安装、更新或卸载的原因
  if (details.reason == "install") {
    // 在安装扩展时执行的代码
  } else if (details.reason == "update") {
    // 在更新扩展时执行的代码
  } else if (details.reason == "uninstall") {
    // 在卸载扩展时执行的代码
  }
});
ca38136c4296540c0a0cef9d97ae230b.png
image.png

设置监听

/**
 * 注意需要声明权限
 */
chrome.bookmarks.onCreated.addListener(function (id, bookmark) {
  console.log(
    "Bookmark created with title: " +
      bookmark.title +
      " and url: " +
      bookmark.url
  );
});
ea660ba23d3154545797c38ea00bde84.png
image.png

过滤事件

/**
 * 注意需要声明 webNavigation 权限
 */
chrome.webNavigation.onCompleted.addListener(() => {
  console.info("The user has loaded my favorite website!");
});

bd6e18bd96754e5e7ae4b5ae6fe0be39.png未过滤时所有的事件都会打印。不同的事件对应不同的功能,需要选择合适的事件来进行监听。

Service Worker 与 Content Script 通信

点击扩展 tab 的事件我们可以通过 chrome.tabs.sendMessage,并在Content Script中注册 chrome.runtime.onMessage.addListener 接收。

颜色提取扩展开发

基于上面的知识点,我们要实现如下功能: 1、导航栏点击扩展按钮弹出一个显示详情的面板 2、在当前页面鼠标点击页面的元素,在弹出的面板上显示对应的背景色 3、点击复制按钮可以复制当前元素背景色

这里我们只列出核心代码如下:

manifet 配置

{
  "manifest_version": 3,
  "name": "EasyColor",
  "description": "Chrome extension for obtaining color in an easy way",
  "version": "0.0.1",
  "action": {
    "default_icon": "images/icon-48.png"
  },
  "icons": {
    "16": "images/icon-16.png",
    "32": "images/icon-32.png",
    "48": "images/icon-48.png",
    "128": "images/icon-128.png"
  },
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["http://*/*", "https://*/*"],
      "css": ["css/styles.css"],
      "js": ["scripts/content.js"],
      "run_at": "document_start"
    }
  ],
  "web_accessible_resources": [
    {
      "resources": ["/pages/panel.html"],
      "matches": ["http://*/*", "https://*/*"]
    }
  ]
}

导航栏点击扩展按钮弹出一个显示面板

首先在 service_worker 中,我们定义一个 background.js,用于监听扩展点击事件,并发送给 content_scripts。

chrome.action.onClicked.addListener(function (tab) {
  //open pages
  chrome.tabs.sendMessage(tab.id, {
    action: "EVENT_PANEL_OPEN",
  });
});

在 content_scripts 中监听 EVENT_PANEL_OPEN 事件并在当前页面增加一个iframe用来显示面板。

chrome.runtime.onMessage.addListener((req, sender, sendResp) => {
  const data = req;
  if (data.action === "EVENT_PANEL_OPEN") {
    let easyPanel = document.getElementById(easyPanelId);
    if (easyPanel == null) {
      easyPanel = document.createElement("iframe");
      easyPanel.id = easyPanelId;
      easyPanel.src = chrome.runtime.getURL("../pages/panel.html");
      easyPanel.style.width = "100%";
      easyPanel.style.height = "100%";
      easyPanel.style.borderRadius = "20px";
      easyPanel.style.border = "none";

      const container = document.createElement("div");
      container.id = easyContainerId;
      container.style.width = "200px";
      container.style.height = "250px";
      container.style.position = "fixed";
      container.style.top = "10px";
      container.style.right = "10px";
      container.style.zIndex = "10000";
      container.style.boxShadow = "3px 2px 22px 1px rgba(0, 0, 0, 0.24)";
      container.style.borderRadius = "20px";
      container.appendChild(easyPanel);

      document.body.appendChild(container);
    }

    document.addEventListener("mousemove", handleMouseMove);

    document.addEventListener(
      "click",
      function (e) {
        e.stopPropagation();
        e.preventDefault();
        return false;
      },
      true
    );
  }
});

点击显示对应颜色

在 content_scripts 中加入点击事件,获取当前元素背景颜色,然后传递给 iframe 进行显示。

document.addEventListener("mousedown", function (e) {
  let easyPanel = document.getElementById(easyPanelId);
  if (easyPanel == null) {
    return;
  }
  const cpStyle = window.getComputedStyle(e.target);
  easyPanel.contentWindow.postMessage(
    {
      type: "computedStyle",
      data: JSON.stringify({
        color: cpStyle.color,
        backgroundColor: cpStyle.backgroundColor,
      }),
    },
    "*"
  );
});

以上就是我们实现的元素颜色选择插件的核心代码,更多细节可以参考:https://github.com/yangpeng7/ChromeExtensionBestPractice

参考资源

https://developer.chrome.com/docs/extensions/mv3/

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

ac7d6df2700b8103827b195d9d66ebe9.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/448077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构考研版——KMP算法

一、 我们先看下面这个算法当比较到不匹配的时候 模式串后移一位&#xff0c;并且让比较指针回去&#xff0c;这就叫做指针的回溯&#xff0c;回溯就是造成这个简单算法效率比较低的原因 这种是朴素模式匹配算法&#xff0c;时间复杂度比较高 int index(Str str,Str substr…

MAVEN安装与配置

文章目录 一、安装MAVEN二、在IDEA中进行配置 一、安装MAVEN 打开MAVEN官网下载&#xff1a;https://maven.apache.org/download.cgi 选择这两个进行下载&#xff0c;然后直接解压缩到指定的安装目录即可。 配置环境变量 1&#xff09;MAVEN_HOME设置为maven的安装目录 2&…

指令段间及文件间参数调用过程(64位 Intel架构)

指令段间及文件间参数调用过程&#xff08;64位 Intel架构&#xff09; 文章目录 指令段间及文件间参数调用过程&#xff08;64位 Intel架构&#xff09;一. 指令段间的参数调用过程1.1 推论1.2 验证 二. 文件间的参数调用过程2.1 推论2.2 验证 三. 指令解释相关补充 一. 指令段…

基于html+css的图片展示24

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

docker安装rocketMQ

1、安装jkd1.8 docker pull java:8 或者 docker pull openjdk:8 查看已安装的镜像&#xff1a; docker images 运行jdk命令 docker run -d -it --name java-8 java:8 进入JDK 容器 docker exec -it java-8 /bin/bash 查看java版本&#xff0c;进入java-8容器后输入 &#x…

金融数字新型基础设施创新开放联合体今日成立

4月18日&#xff0c;“金融数字新型基础设施创新开放联合体”&#xff08;以下简称&#xff1a;联合体&#xff09;在上海成立。联合体由上海银行、复旦大学金融科技研究院、中电金信共同发起&#xff0c;首批成员单位汇聚产业链与供给侧的中坚力量&#xff1a;国泰君安证券、太…

基于 BaiduAI 的人脸检测系统(PyQt5图形化界面实现)

文章目录 写在前面的话总体结构学生端教师端教务处系统 总结 写在前面的话 前几天有个小伙伴私我会不会做关于人脸检测与识别的小项目&#xff0c;奈何我现在主要是学习研究NLP了&#xff0c;所以关于CV的很多东西也有点力不从心&#xff0c;突然想起来去年我的毕业设计就是做…

【Vue】学习笔记-内置指令/自定义指令

内置指令 自定义指令 内置指令v-text 指令v-html指令v-cloak指令v-once指令v-pre指令 自定义指令 内置指令 我们学过的指令&#xff1a; v-bind : 单向绑定解析表达式, 可简写为 :xxx v-model : 双向数据绑定 v-for : 遍历数组/对象/字符串 v-on : 绑定事件监听, 可简写为 v…

遍历思路与子问题思路:详解二叉树的基本操作

二叉树的结构定义&#xff1a; public class BinaryTree {//内部类 表示一个结点static class TreeNode {TreeNode left; //左子树TreeNode right; //右子树char value; //结点值TreeNode(char value) {this.value value;}}public TreeNode root; //根节点... } …

云原生时代下,应用全生命周期管理之道

引言 过去 10 年间&#xff0c;云计算已经从单一的 IT 服务演变成为新一代的软件架构范式&#xff0c;进而赋能企业管理和生产模式的创新。云计算也经历了从“资源上云”到“深度用云”的发展阶段。 在云原生时代&#xff0c;应用全生命周期管理之道成为企业关注的一个焦点。在…

蓝牙耳机什么品牌的音质好?300左右音质最好的蓝牙耳机推荐

随着蓝牙技术的发展&#xff0c;蓝牙耳机品牌也越来越多。要说什么品牌的音质好&#xff1f;首先还是要根据自己的预算出发。在此&#xff0c;我来给大家推荐几款300左右音质最好的蓝牙耳机&#xff0c;可以当个参考。 一、南卡小音舱Lite2蓝牙耳机 参考价&#xff1a;239 发…

Self-supervised learning of a facial attribute embedding from video

Self-supervised learning of a facial attribute embedding from video 译题&#xff1a;视频中人脸属性嵌入的自监督学习 论文题目Self-supervised learning of a facial attribute embedding from video译题视频中人脸属性嵌入的自监督学习时间2018年开源代码地址https://…

《Java》基本类型的比较和引用类型的比较

目录 基本类型 引用数据类型 基本类型之间的比较 基于Comparable的比较 总结 &#x1f451;作者主页&#xff1a;Java冰激凌 &#x1f4d6;专栏链接&#xff1a;Java 基本类型 Java中提供了基本类型有八种 分别是 byte short int long float double char boolean 基本类型…

EL 表达式--各种运算-代码演示--EL 的 11 个隐含对象--pageContext 对象介绍--JSTL 标签库介绍--core 核心库--综合代码

目录 EL 表达式 EL 表达式介绍 代码示例 EL 常用输出形式 代码演示 Book.java el_input.jsp EL 运算操作 基本语法语法&#xff1a; 关系运算 逻辑运算 算数运算 EL 的 empty 运算 应用实例 empty.jsp EL 的三元运算 应用实例 EL 的 11 个隐含对象&#xff0c…

Unity-ML-Agents-训练生成的results文件解读-PushBlock

前言 训练结果文件路径&#xff1a;E:\ml-agents-release_19\results\push_block_test_02&#xff08;具体路径以自己电脑为准&#xff09; ML-Agents安装和PushBlock训练过程请见&#xff1a;&#xff08;注意&#xff1a;push_block_test_02没有全部训练完毕&#xff09; …

同样是测试,朋友到了30k,我才12K,这份测试面试8股文确实牛

程序猿在世人眼里已经成为高薪、为人忠诚的代名词。 然而&#xff0c;小编要说的是&#xff0c;不是所有的程序员工资都是一样的。 世人所不知的是同为程序猿&#xff0c;薪资的差别还是很大的。 众所周知&#xff0c;目前互联网行业是众多行业中薪资待遇最好的&#xff0c;…

推荐几款项目管理工具,提高你的团队协作效率

如何管理团队才能使团队发挥最大的价值&#xff0c;如果团队缺少协作&#xff0c;就会因为团队的内耗和冲突导致项目无法完成&#xff0c;如何提高团队协作效率呢&#xff1f;我们可以借助团队协作类的项目管理工具。 几个常见的项目管理工具&#xff1a; 1、进度猫 进度猫是…

MySQL高级第十五篇:MVCC多版本并发控制原理剖析

MySQL高级第十五篇&#xff1a;MVCC多版本并发控制原理剖析 一、什么是MVCC&#xff1f;二、快照读与当前读&#xff1f;1. 快照读2. 当前读 三、MVCC实现原理&#xff08;ReadView&#xff09;1. 隐藏字段2. Read View3. 思路设计4. ReadView使用规则5. MVCC整体操作流程 四、…

响应式开发HTML5CSS3实现视频播放器的功能案例

目录 前言 一、本视频播放器需要实现的功能 ​二、代码分布结构 三、部分主要代码 1.index01.html 2.video1.css 3.video1.js 四、images图片资源及视频 五、运行效果 前言 1.本文讲解的响应式开发技术&#xff08;HTML5CSS3Bootstrap&#xff09;的HTML5视频播放器等…

随想录Day59--单调栈: 503.下一个更大元素II , 42. 接雨水

看到下一个更大&#xff0c;最先想到的就是单调栈。所以503.下一个更大元素II可以用单调栈的思路进行求解&#xff0c;其实这道题和496.下一个更大元素 I的思路是一样的&#xff0c;不过是多了一个首位相连的环状条件&#xff0c;这时候可以想到&#xff0c;把数组再复制遍历&a…