【Chrome】开发一个Chrome扩展以及常见问题的解决方案

news2024/11/15 9:28:11

前言

本文介绍开发chrome扩展很重要的几种操作,如:操作网页dom、发送请求、渲染弹层、不同沙盒环境的通信方式、扩展与网页的通信方式、遇到iframe时的操作等。最终会提供一个简单的案例,其中涵盖了上述操作。

还有一些本人相关文章,有兴趣也可以看一下,如:判断是否安装了某个Chrome插件、background.js中log打印未出现在控制台、Edge扩展程序上架流程、Chrome扩展程序上架流程。

本文都是在该页面下测试扩展

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script>
    localStorage.setItem("tag", "123456") // 标识是否需要执行插件
  </script>
</head>

<body>
  <input id="test-chrome-extensions" type="text">
</body>

</html>
  • 需要一个标识用于确认是否需要在该页面下运行扩展,本文以本地存储举例,也可能是下面几种方式,需要根据实际场景判断使用。
    • 根据域名判断,可以通过扩展的 manifest.json 中 content_scripts 的 matches 字段来定义允许执行的页面(下文会提及)。
    • 根据url路径参数
    • 根据页面中的特殊标签
    • 根据本地存储
  • 需要一个input输入框用于测试扩展对网页dom元素的直接操作

一、目录结构

开发一个Chrome扩展,项目目录大致如下,下文统一以标准命名介绍。

在这里插入图片描述

  • background.js:后台脚本
  • content.js:内容脚本
  • manifest.json:插件配置文件(清单列表)
  • popup.html 和 popup.js:插件弹窗页面及其脚本

二、配置清单(manifest.json)

  • 该文件必须位于项目的根目录下。
  • 必需的键只有 “manifest_version”、“name” 和 “version”。
  • 开发过程中支持注释 (//),但在将代码上传到 Chrome 应用商店之前,必须先移除这些注释。

基本配置如下

{
  "manifest_version": 3,
  "name": "My Chrome Extension",
  "version": "1.0",
  "permissions": ["storage", "activeTab", "scripting"],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]
}

Chrome官方文档中明确说明,v2已经弃用了。

在这里插入图片描述

在这里插入图片描述

三、沙盒环境

Chrome 插件的每个模块(background.js、popup.js、content.js)都运行在自己的沙盒环境中,意味着它们的 JavaScript 变量和函数是相互隔离的,避免了相互干扰。唯一的通信方式是通过 Chrome 提供的 API(如 chrome.runtime.sendMessagechrome.storage)。

  • background.js:运行在插件的后台,它运行在独立的沙盒环境中,无法与网页内容直接交互
  • popup.html:弹窗页面,用户点击浏览器工具栏的插件图标时弹出。
  • popup.js:只能在弹窗页面存在时运行,弹窗页对应的脚本。
  • content.js:内容脚本,运行在网页的环境中可以直接访问网页的 DOM

浏览器运行环境:与普通的网页脚本不同,Chrome 插件模块无法直接访问网页的全局变量和 DOM,必须通过 content.js 作为桥梁进行交互。同时,插件可以使用 chrome 提供的扩展 API,而网页中的 JavaScript 代码则没有这些权限。

四、通信方式

1. chrome.runtime API

由于处于不同沙盒环境,只能通过chrome.runtime APIchrome.storage通信

chrome.runtime.sendMessage发送消息,接收两个参数

  • message: 要发送的消息内容,可以是一个对象。
  • callback (可选): 当消息成功发送或遇到错误时调用的回调函数。
chrome.runtime.sendMessage({ type: "test", message: "Hello World!" }, (response) => {
  console.log("回应:", response);
});

chrome.runtime.onMessage.addListener监听消息,参数为回调函数,其参数:

  • message:chrome.runtime.sendMessage发送的数据(第一个参数)。
  • sender:发送消息的上下文,包含有关消息来源的信息,如 tab、url、扩展id。
  • sendResponse:返回消息(chrome.runtime.sendMessage中callback可以接收)
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
  console.log("接收到的消息:", message);
  sendResponse({ type: "responseData", data: "返回数据" })
});

2. chrome.storage

不同于 localStoragechrome.storage是异步的,所有存储操作都会接收一个回调函数,以处理操作完成后的逻辑。

chrome.storage 主要有两个存储区域:

  • chrome.storage.local:在本地存储数据,不同步到其他设备,存储容量最大为 5MB。
  • chrome.storage.sync:同步数据到 Chrome 登录账户的所有设备,存储容量为 100KB,适合存储轻量配置或设置。

基本用法

存储数据

// 存储数据
chrome.storage.local.set({ key: "value" }, function() {
  console.log('数据已存储');
});

// 或者使用多个键值对
chrome.storage.local.set({ key1: "value1", key2: "value2" }, function() {
  console.log('多组数据已存储');
});

获取数据

// 获取存储的数据
chrome.storage.local.get(['key'], function(result) {
  console.log('获取到的值:', result.key);
});

// 获取多个键值对
chrome.storage.local.get(['key1', 'key2'], function(result) {
  console.log('key1 的值:', result.key1);
  console.log('key2 的值:', result.key2);
});

删除数据

// 删除指定键的数据
chrome.storage.local.remove('key', function() {
  console.log('数据已删除');
});

// 删除多个键的数据
chrome.storage.local.remove(['key1', 'key2'], function() {
  console.log('多个数据已删除');
});

清除所有数据

// 清除存储中的所有数据
chrome.storage.local.clear(function() {
  console.log('所有数据已清除');
});

测试

进入扩展程序管理页,或者直接访问chrome://extensions/

在这里插入图片描述

确保开启开发者模式

在这里插入图片描述

直接将文件夹拖入即可

注意:background.js不同于content.js,他并不与页面共享相同的 JavaScript 环境,所以需要单独打开一个控制台。

点击检查视图,会弹出background.js对应的控制台

在这里插入图片描述

v2版本的扩展有所不同,名为background.html,新项目不建议使用v2。

在这里插入图片描述

五、案例演示

案例中,content使用chrome.runtime API通信,popup使用chrome.storage获取数据,实际场景中可以使用任意通信方式,没有限制,流程如下:

  1. content 判断该页面是否需要扩展运行
  2. 如果需要则通知 background 发送请求
  3. background 将返回数据通过 chrome.runtime API 交予 content,然后content将该值赋给页面中input元素
  4. background 将返回数据保存到 chrome.storage 供 popup 渲染

在这里插入图片描述

content.js

console.log("content.js - 开始运行...")

const tag = localStorage.getItem("tag")
console.log("标识:", tag)

if (tag) {
  console.log("该页面需要使用插件,准备接收消息...")

  // 通知 background.js 可以继续执行接口调用
  chrome.runtime.sendMessage(
    { type: "pageCheck", valid: true, tag },
    (response) => {
      console.log("收到消息:", response)
      if (response.type === "responseData") {
        const inputElement = document.getElementById("test-chrome-extensions")
        if (inputElement) {
          inputElement.value = response.data.name
        }
      }
    }
  )
} else {
  console.log("该页面不需要使用插件,不执行扩展逻辑。")

  // 通知 background.js 不用调用接口
  chrome.runtime.sendMessage({ type: "pageCheck", valid: false, tag })
}

background.js

console.log("background.js - 开始运行...")

// 模拟请求并返回数据
function fetchData(tag) {
  console.log("标识为", tag) // 可能需要该标识作为请求参数
  const responseData = { name: "田本初", age: 23 }
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(responseData)
    }, 1000) // 模拟接口异步返回
  })
}

// 监听 content.js 发来的消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === "pageCheck") {
    if (message.valid) {
      console.log("页面需要接口调用...")
      fetchData(message.tag).then((response) => {
        chrome.storage.local.set({ data: response }) // 存储数据
        sendResponse({ type: "responseData", data: response }) // 发送消息
        console.log(
          "接口调用成功,向content发送消息,并将data存储到storage供popup使用",
          response
        )
      })
      // 返回 true 表示异步使用 sendResponse,否则content.js 不会等待异步返回结果
      return true
    } else {
      console.log("页面不需要接口调用...")
      return
    }
  }
})

popup.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Popup</title>
  <script src="popup.js"></script>
  <style>
    .container {
      width: 600px;
      height: 600px;
      background-color: #efefef;
    }
  </style>
</head>

<body>
  <div class="container">
    <div id="show_name"></div>
    <div id="show_age"></div>
  </div>
</body>

</html>

popup.js

console.log("popup.js - 开始运行...")

// 获取数据
chrome.storage.local.get(["data"], (result) => {
  if (chrome.runtime.lastError) {
    console.error("获取数据时发生错误:", chrome.runtime.lastError)
    return
  }

  console.log("获取到的值:", result)

  // 处理数据
  const data = result.data

  // 更新 popup.html 中的内容
  const nameDiv = document.getElementById("show_name")
  const ageDiv = document.getElementById("show_age")

  if (nameDiv && ageDiv && data) {
    nameDiv.textContent = `姓名:${data.name}`
    ageDiv.textContent = `年龄:${data.age}`
  }
})

六、分析manifest.json

上文提过一次 manifest.json,通过案例后详细分析一下清单:

{
  "manifest_version": 3,
  "name": "My Chrome Extension",
  "version": "1.0",
  "permissions": ["storage", "activeTab", "scripting"],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]
}
  • manifest_version:Manifest 文件的版本,官方已经不再支持发布v2版,目前都是3。
  • name:插件名
  • version:版本号,后续更新时必须修改在这里插入图片描述
  • permissions:权限,本文只用到了storage
    • activeTab:允许扩展在用户点击扩展图标时访问当前活动标签页的内容
    • scripting:允许扩展使用 chrome.scripting API 注入脚本到网页中
  • background:在后台运行,负责处理扩展的主要逻辑和事件
  • action:用户点击扩展图标时,会显示指定的弹出页面
  • content_scripts:定义要注入的内容脚本及其匹配规则
    • matches:定义哪些网页 URL 匹配规则,以决定哪些页面将注入指定的内容脚本。

      "http://\*/\*":匹配所有 HTTP 协议的网页。
      "https://\*/\*":匹配所有 HTTPS 协议的网页。
      "*://example.com/*":匹配 example.com 域下的所有网页。
      "*://*.example.com/*":匹配 example.com 域下的所有子域网页。
      

修改matches为"matches": ["https://www.baidu.com/"], 可以发现只有在该域名下才执行了 content.js

在这里插入图片描述

配置扩展图标,其中action下的default_icon对应工具栏中的图标,icons对应管理扩展页面中的图标。

"action": {
  "default_popup": "popup.html",
  "default_icon": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
},
"icons": {
  "16": "icons/icon16.png",
  "48": "icons/icon48.png",
  "128": "icons/icon128.png"
},

在这里插入图片描述

在这里插入图片描述

更多详细使用请参考:官方文档

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

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

相关文章

HashMap 详解

哈希表 哈希表又叫散列表&#xff0c;或者映射、字典都是指哈希表&#xff0c;哈希表是通过关键码映射到数组的某个位置来访问的数据结构&#xff0c;实现这个映射的函数就是哈希函数&#xff0c;哈希表结合了数组和链表的优点&#xff0c;查找和插入操作的时间复杂度都是O(1)。…

MySQL篇(高级字符串函数/正则表达式)(持续更新迭代)

目录 讲点一&#xff1a;高级字符串函数 一、简介 二、常见字符串函数 1. CONCAT() 2. SUBSTRING() 3. LENGTH() 4. REPLACE() 5. TRIM() 6. UPPER() 7. LOWER() 8. LEFT() 9. RIGHT() 10. INSTR() 11. LENTH(str) 讲点二&#xff1a;正则表达式 一、简介 二、…

AIGC实战——多模态模型Flamingo

AIGC实战——多模态模型Flamingo 0. 前言1. Flamingo 架构2. 视觉编码器3. Perceiver 重采样器4. 语言模型5. FIamingo 应用小结系列链接 0. 前言 我们已经学习了文本生成图像模型 DALL.E 2&#xff0c;在本节中&#xff0c;我们将探索另一种多模态模型 Flamingo&#xff0c;它…

学习使用在windows系统上安装nodejs以及环境配置图文教程整理

学习使用在windows系统上安装nodejs以及环境配置图文教程整理 Node.js 介绍Node.js 安装1、Node.js下载2、Node.js安装3、Node.js测试4、Node.js安装目录5、Node.js环境变量配置6、配置镜像站&#xff0c;提升速度7、检查镜像站配置8、测试环境变量是否生效9、安装cnpm Node.js…

jwt报错,位置:找不到符号 parseClaimsJws(java.lang.String)

报错显示如图 报错信息为&#xff1a; E:\idea\project\tlias\src\main\java\org\itheima\tlias\utils\JwtUtils.java:36:17 java: 找不到符号 符号: 方法 parseClaimsJws(java.lang.String) 位置: 接口 io.jsonwebtoken.JwtParserBuilder 解决办法 项目使用的是最新…

p12docker 进入容器的命令和拷贝的命令

进入当前正在运行的容器 第一种方式是执行docker exec -it 8d57ffda7a29 /bin/bash这个时候可以根据docker容器的id进入到指定id的容器当中***(这个是比较常用的)*** 老师的笔记 第二种方式是docker attach 8d57ffda7a29 这里还是直接引用老师的笔记吧 从容器内部拷贝文…

HAL库学习梳理——GPIO

笔者跟着B站铁头山羊视频学习 STM32-HAL库 开发教程。有一说一&#xff0c;这个教程自诩为“最佳教程&#xff0c;没有之一~”&#xff0c;确实有点东西。像我这种看视频想睡觉的入门小白来说&#xff0c;感觉捡到宝了。下面对这些课程的应用做一个梳理。 省流&#xff1a; HA…

2-3.Android 存储之存储空间(私有空间、公共空间)

一、内部存储与外部存储 内部存储指位于设备的内部存储空间 外部存储指位于设备的外部存储介质&#xff0c;例如&#xff0c;SD 卡 简单理解&#xff0c;内部存储就是存储在手机自身&#xff0c;外部存储就是存储在手机可以外接的东西&#xff0c;好比电脑的硬盘和 U 盘 二、…

7-1 两个有序链表序列的交集

已知两个非降序链表序列S1与S2&#xff0c;设计函数构造出S1与S2的交集新链表S3。 输入格式: 输入分两行&#xff0c;分别在每行给出由若干个正整数构成的非降序序列&#xff0c;用−1表示序列的结尾&#xff08;−1不属于这个序列&#xff09;。数字用空格间隔。 输出格式:…

『功能项目』切换职业技能面板【49】

我们打开上一篇48切换职业面板的项目&#xff0c; 本章要做的事情是制作第二职业法师技能面板、第三职业面板并且完成切换 双击打开Canvas进入预制体空间 复制三个技能栏面板 重命名 设置第一技能栏 设置第二职业技能栏 设置第三职业技能栏 修改脚本&#xff1a;ChangeProfess…

TS.38-2

2.4.4 空闲模式 如果设备在接收DISPLAY TEXT STK主动命令时处于空闲模式&#xff0c;设备必须从这种待机模式切换到显示相关弹出窗口。一旦STK命令处理完成&#xff08;例如用户选择了“确定”按钮&#xff09;&#xff0c;设备必须返回到其初始状态。 2.4.5 锁屏 - 手动解锁…

Spark实操学习

Spark学习 一、Spark-Shell编程1. 配置python3(三台服务器都要配置)2. 开始Spark编程3. spark-shell工具 二、Java项目测试1. 新建项目2. Spark-java代码测试 三、Scala项目测试1. 安装scala2. 安装包管理器sbt3. 在编译工具中安装scala工具4. 新建项目5. spark-scala代码测试 …

1.MySQL在Centos 7环境安装

目录 MYSQL在Centos7环境安装MySQL环境配置清理环境检查安装包获取官方yum源安装mysql服务配置my.cnf设置开机启动 MYSQL在Centos7环境安装 MySQL环境配置 安装的时候&#xff0c;用户切换成root。初期练习&#xff0c;mysql不进行用户管理&#xff0c;全部使用root进行&#…

【数据结构】排序算法系列——堆排序(附源码+图解)

堆排序 堆排序基于一种常见的**[[二叉树]]结构**&#xff1a;堆 我们前面讲到选择排序&#xff0c;它在待排序的n个记录中选择一个最小的记录需要比较n一1次。本来这也可以理解&#xff0c;查找第一个数据需要比较这么多次是正常的&#xff0c;否则无法知道它是最小的记录。 …

THREE.js:网页上的3D世界构建者

THREE.js&#xff1a;网页上的3D世界构建者 前言 THREE.js 是一个强大的基于 JavaScript 的库&#xff0c;它使得在网页上创建和展示三维图形变得异常简单。 通过封装复杂的 WebGL 技术&#xff0c;THREE.js 提供了一套丰富的 API&#xff0c;让开发者能够轻松地构建出令人印…

Pytorch详解-Pytorch核心模块

Pytorch核心模块 一、Pytorch模块结构_pycache__Cincludelibautogradnnoptimutils 二、Lib\site-packages\torchvisiondatasetsmodelsopstransforms 三、核心数据结构——Tensor&#xff08;张量&#xff09;在深度学习中&#xff0c;时间序列数据为什么是三维张量&#xff1f;…

利用 FastAPI 和 Jinja2 模板引擎快速构建 Web 应用

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;它基于标准 Python 类型提示。FastAPI 支持异步编程&#xff0c;使得开发高性能的 Web 应用变得简单快捷。在本文中&#xff0c;我们将探讨如何使用 FastAPI 结合 Jinj…

单线程与2个线程的简易理解

前言 有个需要10个步骤完成的任务&#xff0c;假设每个步骤需要1秒 单线程耗费10秒完成任务 2根线程可能耗费6秒&#xff0c;也可能更少 单线程程序 单线程下&#xff0c;步骤按照次序顺序执行&#xff0c;共计耗费10秒 2个线程的程序 有步骤可以在同一时刻同时运行&…

Python酷库之旅-第三方库Pandas(117)

目录 一、用法精讲 516、pandas.DataFrame.add_suffix方法 516-1、语法 516-2、参数 516-3、功能 516-4、返回值 516-5、说明 516-6、用法 516-6-1、数据准备 516-6-2、代码示例 516-6-3、结果输出 517、pandas.DataFrame.align方法 517-1、语法 517-2、参数 51…

Linux操作系统如何添加新字体

在一个Linux操作系统及办公软件刚安装后&#xff0c;会发现缺少常用的“楷体_GB2312”和“仿宋_GB2312”字体。此时&#xff0c;只需要从其它电脑复制到或者从互联网上下载到这两个字体文件&#xff0c;然后导入到自己的电脑即可&#xff0c;再次打开办公软件就会看到这个字体已…