三万+字符数:从 0 到 1 带你开发 Chrome 浏览器 Manifest V3 版本插件

Chrome 插件

一、什么是 Chrome 浏览器插件?

Chrome 浏览器插件是一种能够增强和定制 Chrome 浏览器功能的软件组件。

Chrome 浏览器插件可通过自定义界面、观察浏览器事件和修改网络来提升浏览体验。

二、Chrome 浏览器插件是如何构建的?

使用 Web 技术开构建 Chrome 插件:HTML、CSS、JS。

三、Chrome 浏览器插件可以做什么?

1. 设计界面

大多数扩展程序都需要某种类型的用户互动才能正常运行。扩展程序平台提供了多种方式来向您的扩展程序添加互动。这些方法包括从 Chrome 工具栏、侧边栏、上下文菜单等触发的弹出式窗口:

  1. 侧边栏(Side panel)
  2. 操作项(Action)
  3. 菜单项(Menus)

2. 控制浏览器

借助 Chrome 的扩展程序 API,可以改变浏览器的工作方式:

  1. 覆盖 Chrome 页面和设置项:Manifest.json 配置 chrome_settings_overrides
  2. 扩展开发者工具:Manifest.json 配置 devtools_page
  3. 显示通知:chrome.notifications API
  4. 管理历史记录:chrome.history API
  5. 控制标签页和窗口:chrome.tabs、chrome.tabGroups 和 chrome.windows 等 API
  6. 键盘快捷键:chrome.commands API
  7. 身份认证:chrome.identity API
  8. 管理插件:chrome.management API
  9. 提供建议:chrome.omnibox API
  10. 更新 Chrome 设置:chrome.proxy API
  11. 下载管理:chrome.downloads API
  12. 书签:chrome.bookmarks API
  13. ...

3. 控制网络

可以通过注入脚本、拦截网络请求以及使用 Web API 与网页进行交互,来控制和修改 Web:

  1. 注入 JS 和 CSS 文件
  2. 访问当前 Tab 页
  3. 控制 Web 请求
  4. 录音和屏幕截图
  5. 修改网站设置

四、Chrome 插件术语

1. 清单(Manifest)

Chrome 插件的清单是唯一且必须具有特定文件名的必需文件:manifest.json;

该文件列出了有关该扩展程序的结构和行为的重要信息。

1.1 示例展示

json 复制代码
{
  "manifest_version": 3,
  "name": "My Chrome Extension",
  "version": "0.0.1",
  "description": "My Chrome Extension Description"
}

1.2 manifest.json 文件必需的字段

  1. manifest_version:用于指定扩展程序使用的清单文件格式版本,目前是 3
  2. name:插件名称,一般情况下 hover 插件图标展示的文案也是 name
  3. version:插件版本

1.3 发布 Chrome 应用商店需要的字段

  1. description:插件描述
  2. icons:图标
  1. name
  2. version
  3. description
  4. icons

2. 操作项(Action)

控制扩展程序在 Chrome 浏览器工具栏中的图标。

2.1 示例

json 复制代码
{
  "manifest_version": 3,
  "name": "My Chrome Extension",
  "version": "0.0.1",
  "description": "My Chrome Extension Description",
  "icons": {
    "16": "icons/icon_16.png",
    "32": "icons/icon_32.png",
    "48": "icons/icon_48.png",
    "128": "icons/icon_128.png"
  },
  "action": {
    "default_icon": "icons/icon.png",
    "default_title": "Popup Title",
    "default_popup": "popup.html"
  }
}
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    *{
      padding: 0;
      margin: 0;
    }
    div{
      width: 300px;
      height: 300px;
      text-align: center;
      line-height: 300px;
      background: gray;
    }
  </style>
</head>
<body>
  <div>popup html</div>
</body>
</html>

2.2 Action 字段

  1. default_icon:工具栏展示的图片
  2. default_title:hover 插件图标展示的文案
  3. default_popup:点击图标弹出的页面

3. 背景(Background - Service Worker)

Service Worker 在后台运行并处理浏览器事件

在 V3 中使用 service worker 替换 background 页面

3.1 Background 在 manifest V2 V3 版本中的不同之处

  1. V2 版本 background
json 复制代码
{
  "background": {
    "scripts": [
      "backgroundContextMenus.js",
      "backgroundOauth.js"
    ],
    "persistent": false
  },
}
  1. V3 版本 background
json 复制代码
{
  "background": {
    "service_worker": "service_worker.js",
    "type": "module"
  }
}

3.2 示例

在上面的 Action 示例中添加以下代码

json 复制代码
"background": {
  "service_worker": "service_worker.js"
}
  1. 激活 Service Worker
  1. 终止 Service Worker

3.3 终止 Service Worker

  1. 无操作 30 秒后。
  2. 单个请求的处理用时超过 5 分钟。
  3. fetch 响应的传递时间超过 30 秒时。

3.4 Service Worker DevTools

点击 Chrome Service Worker 即可弹出 DevTools

4. 内容脚本(Content scripts)

在网页环境中运行 JavaScript 或 CSS。 通过 content_scripts 进行注册。

4.1 示例

  1. 在上面的 Service Worker 示例中添加以下代码
json 复制代码
"content_scripts": [
  {
    "matches": ["https://lkcoffee.com/"],
    "js": ["content_scripts.js"]
  }
]
  1. content_scripts.js 文件代码
typescript 复制代码
console.log('this is content scripts')

4.2 页面注入

  1. 打开 lkcoffee.com/,并打开控制台
  1. 点击 content_scripts.js

五、开发 Chrome 插件

1. 新建项目并创建 manifest.json 文件

1.1 添加以下代码

json 复制代码
{
  "manifest_version": 3,
  "name": "My Chrome Extension",
  "version": "0.0.1",
  "description": "My Chrome Extension Description"
}

1.2 在谷歌浏览器中打开管理扩展程序

  1. 第一种方式
  1. 第二种方式
  1. 第三种方式

浏览器输入:chrome://extensions/

1.3 点击【加载已解压的扩展程序】

选择刚刚创建的文件夹即可

1.4 可以看到扩展程序已经加载完成

包含名称、版本、描述、默认图标

1.5 固定插件

  1. 点击固定按钮,即可把插件固定在工具栏中
  1. 固定之后的样式

到了这一步,一个最最基础的插件已经完成了

以谷歌翻译模版为例

2.1 添加 action icons

创建 icons 文件夹,并添加图片

shell 复制代码
.
├── icons
│   └── icon.png
├── manifest.json
2.2.1 同时创建 index.css 和 index.js 文件

目前的目录结构

shell 复制代码
.
├── icons
│   └── icon.png
├── manifest.json
└── popup
    ├── index.css
    ├── index.html
    └── index.js
  1. index.html
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="./index.css">
</head>
<body>
  <div class="plugin_search">
      <div class="plugin_search_bar">
          <input id="plugin_search_inp" />
          <input id="plugin_search_but" type="button" value="翻译" />
      </div>
      <div class="plugin_span">
          <span>翻译此页面</span>
      </div>
  </div>
  
  <div class="plugin_footer">
      <span>© 2015 Google -</span>
      <a target="_blank" href="https://google.com/">扩展程序选项</a>
      <a target="_blank" href="https://translate.google.com/?source=gtx">Google 翻译</a>
  </div>
</body>
<script src="./index.js"></script>
</html>
  1. index.css
css 复制代码
*{
  padding: 0;
  margin: 0;
  list-style: none;
  text-decoration: none;
}
.plugin_search{
  margin: 15px 30px 15px 30px;
}
.plugin_search_bar{
  margin: 8px 0 16px 0;
}
#plugin_search_inp{
  font-size: 14px;
  margin: 2px;
  height: 23px;
  width: 70%;
}
#plugin_search_but{
  box-shadow: none;
  background-color: #4d90fe;
  background-image: -webkit-linear-gradient(top,#4d90fe,#4787ed);
  border: 1px solid #3079ed;
  color: #fff;
  border-radius: 2px;
  cursor: default;
  font-size: 11px;
  font-weight: bold;
  text-align: center;
  white-space: nowrap;
  margin-right: 16px;
  height: 27px;
  line-height: 27px;
  width: 54px;
  outline: 0px;
  padding: 0 8px;
  box-sizing: content-box;
}
.plugin_span{
  color: #4285f4;
}
.plugin_footer{
  background-color: #f0f0f0;
  color: #636363;
  font-family: 'Roboto',sans-serif;
  font-size: 11px;
  margin-top: 9px;
  padding: 10px;
  text-align: center;
  width: 400px;
}
.plugin_footer a{
  color: #4285f4;
}
  1. index.js
javascript 复制代码
const plugin_search_but = document.getElementById('plugin_search_but')
const plugin_search_inp = document.getElementById('plugin_search_inp')
plugin_search_but.onclick = function () {
    alert('plugin_search_inp的值为:' + plugin_search_inp.value.trim())
}

2.3 Manifest.json 文件添加 action 字段

2.3.1 Manifest.json 文件内容
json 复制代码
{
  "manifest_version": 3,
  "name": "My Chrome Extension",
  "version": "0.0.1",
  "description": "My Chrome Extension Description",
  "action": {
    "default_icon": "icons/icon.png",
    "default_title": "Popup Title",
    "default_popup": "popup/index.html"
  }
}
2.3.2 点击扩展程序刷新按钮

可以看到小图标已经展示出来了

2.3.3 Hover 图标展示 default_title 字段
2.3.4 点击图标

可以看到我们的 html 和 css 已经生效了

2.3.5 输入框输入,并点击翻译按钮

根据我们 index.js 文件中的代码,我们可以在输入框中输入文案,并点击翻译按钮

原来这就是惊喜

点击图标,弹出 popup 页面,右键,会有【检查】选项,点击检查按钮

到了这一步,我们可以开发一个有点击动作的插件了

3. 添加 background(service worker)

javascript 复制代码
setInterval(() => {
  alert('哈哈哈哈,我又出来了')
}, 3000)

保存代码之后,刷新插件,点击插件图标弹出 popup 页面,就会定时弹出 alert 框了

但是,当我们点击页面某处,把插件 popup 页面隐藏的时候,就会发现 alert 弹框不会再弹出了,只有在此展现 popup 页面的时候才会重新执行

那如果我想的是不展现 popup 的时候,也执行 alert 弹框,那就需要 background 了

3.2 新建 background 文件夹并创建 service_worker.js 文件

  1. 先把 popup index.js 中的 alert 删除
  2. 在 service_worker.js 中写入代码
typescript 复制代码
setInterval(() => {
  alert('我是 background service worker 中弹出来的')
}, 3000)
  1. 目录结构
shell 复制代码
.
├── background
│   └── service_worker.js
├── icons
│   └── icon.png
├── manifest.json
└── popup
    ├── index.css
    ├── index.html

3.3 Manifest.json 文件中添加 background

3.3.1 在上面 action 的基础上添加代码
json 复制代码
"background": {
  "service_worker": "background/service_worker.js"
}
3.3.2 点击拓展程序刷新插件
  1. 发现会有错误出现
  1. 我们先点击检查视图后面的 Service Worker 弹出控制台
  1. 可以看出报错内容

这是因为 Service Workers 在插件中主要用于后台任务、推送通知等,而不是直接与用户交互。因此,alert 这类弹窗通常无法直接在 Service Worker 中使用。

  1. 我们点击【错误】按钮,你会发现,每隔三秒会刷新当前错误页面,是因为一直在执行 service_worker.js 中的代码
  1. 但是当 30s 之后,会发现不刷新了,这个时候我们点击返回,可以看到 service worker 已经被终止了

这个时候如果继续点击 service worker 则会继续触发当前程序

3.4 既然不能用 alert,那我们就换个 notifications 用一用

借助 chrome.notifications API,可以通过模板创建内容丰富的通知,并在系统任务栏中向用户显示这些通知。

3.4.1 修改 service worker 中的代码
typescript 复制代码
setInterval(() => {
  chrome.notifications.create(
    {
      type: "basic",
      title: "Notifications Title",
      message: "Notifications message to display",
      iconUrl: "../icons/icon.png"
    },
    (notificationId) => {
      console.log('notificationId-->', notificationId)
    }
  );
}, 3000)
3.4.2 刷新插件

会发现还是会报错

说是 notifications 未定义

这个时候就涉及到 manifest.json 中的另一个字段了,权限(permissions)字段,使用 Chrome.XXX 的 API 绝大多数的时候都需要在 permissions 字段中添加,permissions 是一个 string[]

3.4.3 manifest.json 文件中添加 permissions 字段

添加如下代码

json 复制代码
"permissions": [
  "notifications"
]
3.4.4 刷新插件,并点开 service worker 控制台

可以看到控制台中已经有通知的回调返回值了

那通知呢?

3.4.5 平台差异

平台差异 :对于 Mac OS X 用户,通知的显示方式有所不同。用户看到的不是 Chrome 自己的通知,而是原生 Mac OS X 通知。

点开通知中心,即可看到通知

3.5 如果有多个 JS 需要引入怎么办?

如果还需要引入 module_1.js 和 module_2.js

3.5.1 manifest.json 中 background 字段添加 type 为 module
json 复制代码
"background": {
  "service_worker": "background/service_worker.js",
  "type": "module"
},
3.5.2 在 service_worker.js 中 import 引入
javascript 复制代码
import './module_1.js'
import './module_2.js'
3.5.3 刷新插件,打开 Service worker 控制台

每个 js 里面只是输出了一句话

到这一步,我们已经可以操作后台了

4. Action(popup) 和 background(service worker) 之间的通信

点击翻译的时候,发一条信息

4.1 先打印下 popup.js 中的 Chrome 数据

typescript 复制代码
plugin_search_but.onclick = function () {
  // alert('plugin_search_inp的值为:' + plugin_search_inp.value.trim())
  console.log('chrome', chrome)
}

4.2 先把 popup.js 中的点击事件修改下

改成点击之后向 Service Worker 发送消息

javascript 复制代码
plugin_search_but.onclick = function () {
  // alert('plugin_search_inp的值为:' + plugin_search_inp.value.trim())
  console.log('chrome', chrome)
  // 向 Service Worker 发送消息
  chrome.runtime.sendMessage({
    action: 'fromPopup',
    message: 'Hello from Popup!'
  });
}

4.3 只需要在 service_worker.js 中接收消息即可

把之前的代码注释/删除即可

typescript 复制代码
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
  if (message.action === 'fromPopup') {
    chrome.notifications.create(
      {
        type: "basic",
        title: "Notifications Title",
        message: "Notifications message to display",
        iconUrl: "../icons/icon.png"
      },
      (notificationId) => {
        console.log('notificationId-->', notificationId)
      }
    );
  }
});

4.4 刷新插件

点击图标,弹出 popup 页面,点击翻译按钮,点击消息中心

5. 内容脚本(Content Script)注入

如果需要在某个页面中注入脚本进去,那就用到了 content_scripts

更新 content script 之后需要刷新对应的页面

5.1 新建 content 文件夹,并创建 index.js 文件

  1. 写入代码
javascript 复制代码
console.log('this is content js')
console.log('document', document)
console.log('location', location)
console.log('window', window)
  1. 目录结构
shell 复制代码
.
├── background
│   ├── module_1.js
│   ├── module_2.js
│   └── service_worker.js
├── content
│   └── index.js
├── icons
│   └── icon.png
├── manifest.json
└── popup
    ├── index.css
    ├── index.html
    └── index.js

5.2 在 manifest.json 文件中添加 content_scripts

json 复制代码
"content_scripts": [
  {
    "matches": ["https://lkcoffee.com/"],
    "js": ["content/index.js"]
  }
]

可以看出我们引入了 js 文件和匹配了域名

5.3 打开 lkcoffee.com/ 并打开控制台

5.4 在当前页面中插入面板框,并可拖拽

使用 jquery

  1. 在 content 中创建 index.css 和 jquery.js(可在 CDN 上下载)
  2. 在 index.css 上写入面板样式
  3. 在 index.js 上创建 dom 和监听鼠标事件
  4. 在 manifest.json 中引入

manifest.json 文件

json 复制代码
"content_scripts": [
  {
    "matches": ["https://lkcoffee.com/"],
    "css": ["content/index.css"],
    "js": ["content/jquery.js", "content/index.js"]
  }
]

JS/CSS 注入多个时,按照数组顺序注入,所以如果 JS/CSS 有多个的话,需要注意引用关系。 index.css 文件

css 复制代码
#cj_move_page{
  width: 400px; 
  user-select: none; 
  background: white; 
  border: 1px solid; 
  height: 400px; 
  position: fixed; 
  right: 0; 
  bottom: 0; 
  z-index: 1000001;
}
#cj_move_h3{
  text-align: center; 
  line-height: 40px; 
  cursor: move;
}

index.js 文件

css 复制代码
console.log('this is content js')
console.log('document', document)
console.log('location', location)
console.log('window', window)

//创建页面函数
function createPage () {
  const page = $('<div id="cj_move_page"></div>')
  const h3 = $('<h3 id="cj_move_h3">my Plugin</h3>')
  page.append(h3)
  $('body').append(page)
  //拖拽
  drag(cj_move_h3)
}
createPage()

//拖拽
function drag(ele) {
  let oldX, oldY, newX, newY
  ele.onmousedown = function (e) {
    if (!cj_move_page.style.right && !cj_move_page.style.bottom) {
      cj_move_page.style.right = 0
      cj_move_page.style.bottom = 0
    }
    oldX = e.clientX
    oldY = e.clientY
    document.onmousemove = function (e) {
      newX = e.clientX
      newY = e.clientY
      cj_move_page.style.right = parseInt(cj_move_page.style.right) - newX + oldX + 'px'
      cj_move_page.style.bottom = parseInt(cj_move_page.style.bottom) - newY + oldY + 'px'
      oldX = newX
      oldY = newY
    }
    document.onmouseup = function () {
      document.onmousemove = null
      document.onmouseup = null
    }
  }
}

5.5 刷新 Chrome 插件,并刷新匹配的页面

可以看到右下角有个 content 页面

到这一步我们已经可以注入自己想要的东西了

6. Content 和 background(Service Worker)通信

如果我想在 content 中点击按钮来触发消息通知要怎么做呢?

6.1 在刚才的面板页面加一个 button,再加一个 click 事件

先打印下 Chrome 数据,看看有啥

typescript 复制代码
//创建页面函数
function createPage () {
  const page = $('<div id="cj_move_page"></div>')
  const h3 = $('<h3 id="cj_move_h3">My Chrome Ext Content Page</h3>')
  const but1 = $('<button id="cj_but1">消息通知</button>')
  page.append(h3)
  page.append(but1)
  $('body').append(page)
  $('#cj_but1').click(async (e) => {
    console.log('e', e, chrome)
  })
  //拖拽
  drag(cj_move_h3)
}

6.2 刷新插件,点击 button 触发事件

可以看到 chrome 有个 runtime,里面有 sendMessage 可以发送消息,有 onMessage 可以接收消息

6.3 在 content_scripts 里面进行消息发送

把 click 事件改一下,加一个消息发送

typescript 复制代码
$('#cj_but1').click(async (e) => {
  console.log('e', e, chrome)
  // 发送消息
  chrome.runtime.sendMessage({action: "fromContent"});
})

6.4 Service_worker.js 里面进行消息接收

和上面 action 和 service-worker 的通信一样,在一个 if 判断就行

typescript 复制代码
if (message.action === 'fromContent') {
  chrome.notifications.create(
    {
      type: "basic",
      title: "Notifications Title",
      message: "Notifications message to display",
      iconUrl: "../icons/icon.png"
    },
    (notificationId) => {
      console.log('notificationId-->', notificationId)
    }
  );
}

6.5 刷新插件

刷新插件,点击按钮,查看消息通知

7. Action(popup) 和 content 通信

因为 content 是注入页面的脚本,所以和 content 通信,需要获取当前 tab 信息

就需要权限(permissions)

7.1 添加 tab 权限(permissions)

添加权限的方式有两种

  1. permissions 中添加 tabs
json 复制代码
"permissions": [
  "notifications",
  "tabs"
],
  1. 另一种是在主机权限(host_permissions)添加匹配的域名
json 复制代码
"host_permissions": [
  "https://movie.douban.com/"
]

推荐使用第二种,这种符合权限最小化

7.2 获取 tab 数据

需要在 popup js 中获取 tab 数据

typescript 复制代码
const [tab] = await chrome.tabs.query({
  url: ["https://movie.douban.com/*"],
  active: true,
  currentWindow: true
});
console.log('tab', tab)

tab 信息

  1. popup 代码
typescript 复制代码
const plugin_search_but = document.getElementById('plugin_search_but')
const plugin_search_inp = document.getElementById('plugin_search_inp')
plugin_search_but.onclick = async function () {
  // alert
  // alert('plugin_search_inp的值为:' + plugin_search_inp.value.trim())
  // console.log('chrome', chrome)
  // 向 Service Worker 发送消息
  // chrome.runtime.sendMessage({
  //   action: 'fromPopup',
  //   message: 'Hello from Popup!'
  // });

  const [tab] = await chrome.tabs.query({
    url: ["https://movie.douban.com/*"],
    active: true,
    currentWindow: true
  });
  console.log('tab', tab)
  if (tab) {
    // 使用 chrome.tabs.sendMessage 发送消息
    chrome.tabs.sendMessage(tab.id, {
      action: 'fromPopup2Content'
    })
  }
}
  1. content 监听代码
javascript 复制代码
chrome.runtime.onMessage.addListener((e) => {
  console.log('e', e)
})
  1. 刷新插件,刷新页面,打开控制台,点击图标,弹出 popup,点击翻译按钮
  1. popup 代码
typescript 复制代码
if (tab) {
  // 使用 chrome.tabs.sendMessage 发送消息
  // chrome.tabs.sendMessage(tab.id, {
  //   action: 'fromPopup2Content'
  // })

  const connect = chrome.tabs.connect(tab.id, {name: 'fromPopup2Content'});
  console.log('connect', connect)
  connect.postMessage('这里是弹出框页面,你是谁?')
  connect.onMessage.addListener((mess) => {
    console.log(mess)
  })
}
  1. content 代码
typescript 复制代码
// chrome.runtime.onMessage.addListener((e) => {
//   console.log('e', e)
// })


chrome.runtime.onConnect.addListener((res) => {
  console.log('contentjs中的 chrome.runtime.onConnect:',res)
  if (res.name === 'fromPopup2Content') {
    res.onMessage.addListener(mess => {
      console.log('contentjs中的 res.onMessage.addListener:', mess)
      res.postMessage('哈哈哈,我是contentjs')
    })
  }
})
  1. 刷新插件,刷新页面,打开控制台,点击图标,弹出 popup,点击翻译按钮

content 页面的日志

popup 页面的日志

8. Fetch 请求

V3 版本已经禁止使用 XMLHttpRequest,所有的请求走的都是 fetch

8.1 我们现在 content_scripts 中的 matches 中加入豆瓣域名(www.douban.com)

用于获取数据

*: 通配符匹配

json 复制代码
"content_scripts": [
  {
    "matches": ["https://lkcoffee.com/", "https://movie.douban.com/*"],
    "css": ["content/index.css"],
    "js": ["content/jquery.js", "content/index.js"]
  }
]

8.2 在刚才的 index.js 文件中加入按钮,点击的时候触发 fetch 请求

只更改了 createPage 函数,增加了 but2、but3 按钮和 click 事件

javascript 复制代码
//创建页面函数
function createPage () {
  const page = $('<div id="cj_move_page"></div>')
  const h3 = $('<h3 id="cj_move_h3">My Chrome Ext Content Page</h3>')
  const but1 = $('<button id="cj_but1">消息通知</button>')
  const but2 = $('<button id="cj_but2">content 加载更多</button>')
  const but3 = $('<button id="cj_but3">service worker 加载</button>')
  page.append(h3)
  page.append(but1)
  page.append(but2)
  page.append(but3)
  $('body').append(page)
  // 消息通知按钮事件
  $('#cj_but1').click(async (e) => {
    console.log('e', e, chrome)
    chrome.runtime.sendMessage({action: "fromContent"});
  })
  // content 加载更多按钮事件
  $('#cj_but2').click(async (e) => {
    const response = await fetch("https://movie.douban.com/j/tv/recommend_groups")
    if (!response.ok) {
      throw new Error('Network response was not ok')
    }
    const allData = await response.json()
    console.log('content index allData', allData)
  })
  // service worker 加载按钮事件
  $('#cj_but3').click(async (e) => {
    console.log('e', e, chrome)
    chrome.runtime.sendMessage({action: "fromContentFetch"});
  })
  //拖拽
  drag(cj_move_h3)
}

8.3 service_worker.js 里面进行消息监听

typescript 复制代码
if (message.action === 'fromContentFetch') {
  const response = await fetch("https://movie.douban.com/j/tv/recommend_groups")
  if (!response.ok) {
    throw new Error('Network response was not ok')
  }
  const allData = await response.json()
  console.log('service worker allData', allData)
}

8.4 刷新插件并刷新匹配的豆瓣域名页

8.5 点击按钮

  1. 打开页面控制台,点击【content 加载更多】
  1. 打开 service worker 控制台,点击【service worker 加载】

到这一步,我们已经可以进行接口请求了,整体的链路已经通了

web 开发少不了获取 cookie
使用 chrome.cookies API 查询和修改 Cookie,并在这些 Cookie 发生更改时收到通知。

这就需要在权限(permissions)里面添加 cookies 字段以及在主机权限(host_permissions)里面添加 host

9.1 Manifest.json 文件

ts 复制代码
"permissions": [
    "notifications",
    "cookies"
],
"host_permissions": [
    "https://*.douban.com/*",
    "https://*.lkcoffee.com/*"
]
typescript 复制代码
const cookies = await chrome.cookies.getAll({ domain:  '.lkcoffee.com'})
console.log('popup cookies--->', cookies)
const urlCookies = await chrome.cookies.getAll({ url: 'https://leaptest03.lkcoffee.com/' })
console.log("popup urlCookies", urlCookies);

9.3 Service Worker 获取 cookies

typescript 复制代码
const cookies = await chrome.cookies.getAll({ domain:  '.lkcoffee.com'})
console.log('service worker cookies--->', cookies)

9.4 Content 获取 cookies

这个直接用 document.cookie 获取即可

六、打包 Chrome 插件

1. 不需要发布 Chrome 应用商店

直接压缩包就行,安装的时候加载已解压的扩展程序即可

2. 需要发布到 Chrome 应用商店

  1. 前往 Chrome 开发者信息中心。
  2. 登录开发者帐号。
  3. 点击添加新商品按钮。
  4. 依次点击选择文件 > ZIP 文件 > 上传。

gg

七、总结

  1. 到此基本上已经可以开发一个完整流程的 Chrome 插件了,但是这篇文章只是一个基础
  2. Manifest.json 文件字段解析
  3. 权限列表(permissions)
  4. 匹配模式规则
  5. Content 脚本动态注入
  6. Chrome API
  7. V2 迁移 V3 版本
  8. ...

引用

相关推荐
鬼手点金4 小时前
nanoGPT/nanochat 能做出 DeepSeek/Qwen 这样的大模型吗?
服务器·前端·javascript·bug·openclaw
蛋蛋的就会顺顺的4 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
程序员Sunday4 小时前
TypeScript 的 any、unknown、never 有什么区别?
前端·面试·typescript
朱涛的自习室5 小时前
70 美金的 Beyond Compare,我让 Gemini 4 和 DeepSeek 造个开源免费版(Day 2)
android·前端·人工智能
码艺-Alimjan5 小时前
Tauri 源码备份 · 打包 & 解压 总结,源码收集之路
前端·javascript·vue.js
小马不起床5 小时前
零基础玩转 FastAPI_11:前端后台管理页面
前端·状态模式·fastapi
子非鱼a7 小时前
【文件上传】一个普普通通的edu16
前端
美狐美颜SDK开放平台7 小时前
直播APP如何实现美白滤镜功能?视频美颜SDK技术开发方案详解
前端·数据库·人工智能·音视频·美颜sdk·直播美颜sdk·视频美颜sdk
pride.li8 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
屏什么录8 小时前
电脑如何录屏幕视频和声音?从工具到代码,7 种方案帮你彻底搞定
前端·音视频开发