后端开发者的谷歌插件开发入门指南(二)

开发第一个插件

为了让大家更快速、更直观地了解插件开发的入门过程,我们将参考官方的Demo案例作为指导。这些Demo案例不仅为你展示了插件的基本结构和功能,还提供了清晰的开发步骤和代码示例。无论你是初学者还是有一定经验的开发者,这个案例都能帮助你迅速掌握插件开发的核心概念和技术。

Hello World 扩展程序

概览

您将创建一个"Hello World"示例,在本地加载扩展程序,查找日志。

当用户点击扩展程序工具栏图标时,此扩展程序会显示"Hello Extensions"。

构建

  1. 创建 manifest.json 文件。
json 复制代码
{
  "manifest_version": 3,
  "name": "Hello Extensions",
  "description": "Base Level Extension",
  "version": "1.0",
  "action": {
    "default_popup": "hello.html"
  }
} 
  1. 创建 hello.html 文件。
css 复制代码
<html>
  <body>
    <h1>Hello Extensions</h1>
  </body>
</html>
  1. 加载已解压的扩展程序。
  • 在新标签页中输入 chrome://extensions,前往"扩展程序"页面。

    • 或者,您也可以点击"扩展程序"菜单谜题按钮,然后选择菜单底部的管理扩展程序
    • 或者,点击 Chrome 菜单,将光标悬停在更多工具 上,然后选择扩展程序
  • 点击开发者模式旁边的切换开关以启用开发者模式。
  • 点击 Load unpacked(加载解压缩)按钮,然后选择扩展程序目录。
  • 默认情况下,在本地加载扩展程序时,它会显示在扩展程序菜单中。将扩展程序固定到工具栏,以便在开发过程中快速访问扩展程序。
  • 点击扩展程序的操作图标(工具栏图标);您应该会看到一个弹出式窗口。

重新加载

返回代码,在清单中将扩展程序的名称更改为"Hello Extensions of the world!"。

json 复制代码
{
  "manifest_version": 3,
  "name": "Hello Extensions of the world!",
  ...
}

保存文件后,要在浏览器中看到此更改,您还需要刷新扩展程序。前往"扩展程序"页面,然后点击开启 /关闭切换开关旁边的刷新图标。

下表显示了需要重新加载哪些组件才能看到更改:

扩展程序组件 需要重新加载扩展程序
清单
Service Worker
内容脚本
弹出式窗口
选项页面
其他扩展程序 HTML 网页

查看控制台日志与错误

在开发期间,您可以通过访问浏览器控制台日志来调试代码。在本例中,我们将找到该弹出式窗口的日志。首先,向 hello.html 添加脚本标记。

xml 复制代码
<html>
  <body>
    <h1>Hello Extensions</h1>
    <script src="popup.js"></script>
  </body>
</html>

创建一个popup.js文件,并添加以下代码。

arduino 复制代码
console.log("This is a popup!")

要在控制台中查看这条消息的记录,请按以下步骤操作:

  1. 打开弹出式窗口。
  2. 右键点击弹出式窗口。
  3. 选择检查
  1. 在 DevTools 中,前往控制台面板。

在每个网页上运行脚本

概览

创建您的首个用于在网页上插入新元素的扩展程序。每次打开一个新页面时,都将弹出一个对话框。

构建

  1. 创建 manifest.json 文件,并声明注册 content.js 脚本。其中"matches" 字段可以有一种或多种匹配模式。这些变量可让浏览器识别要将内容脚本注入到哪些网站。匹配模式由三部分组成:<scheme>://<host><path>。它们可以包含"*"字符。
json 复制代码
{
    "manifest_version": 3,
    "name": "Hello Extensions",
    "description": "Base Level Extension",
    "version": "1.0",
    "action": {
      "default_popup": "hello.html"
    },
    "content_scripts": [
      {
        "matches": ["<all_urls>"], //这里我们匹配所有路径
        "js": ["content.js"]
      }
    ]
  } 
  1. 编写 content.js 内容。
scss 复制代码
alert('hello world')
  1. 加载扩展程序。
  2. 随便找到一个网页打开,当页面加载完成时会弹出一个 hello world 的弹窗。

将脚本注入当前标签页中

概览

点击扩展程序工具栏图标,简化当前页面的样式设置。去除当前页面中所有的图片元素。

构建

  1. 创建 manifest.json 文件。
json 复制代码
{
  "manifest_version": 3,
  "name": "Focus Mode",
  "description": "Enable focus mode on Chrome's official Extensions and Chrome Web Store documentation.",
  "version": "1.0"
}
  1. 初始化扩展程序。

扩展程序可以使用扩展程序的 Service Worker 在后台监控浏览器事件。Service Worker 是特殊的 JavaScript 环境,用于处理事件,并会在不需要时终止。(Service Worker 其实就是上文中讲到的 Background Scripts ,在 Manifest V2 版本中,因为 Background Scripts 会长期存在于后台中,会占用资源。因此在 V3中将后台上下文移至仅在需要时运行的 Service Worker。Service Worker 用于处理事件,并会在不需要时终止。)

css 复制代码
{
  ...
  "background": {
    "service_worker": "background.js"
  },
  ...
}

创建 background.js 文件。

arduino 复制代码
chrome.runtime.onInstalled.addListener(() => {
  chrome.action.setBadgeText({
    text: "OFF",
  });
});

这里我们利用 Service Worker 监听 runtime.onInstalled()。此方法可让扩展程序在安装时设置初始状态或完成一些任务。我们通过 setBadgeText 去设置扩展图标上的徽章(badge)文本。

  1. 启用扩展程序操作。

扩展程序操作 可控制扩展程序的工具栏图标。因此,只要用户点击该扩展程序图标,它就会运行一些代码(如此示例所示)或显示一个弹出式窗口。添加以下代码,以在 manifest.json 文件中声明扩展程序操作:

json 复制代码
  "action": {}
  1. 声明权限。

由于我们会修改用户访问页面的样式,因此也需要在 manifest 中声明 activeTab 权限。

json 复制代码
  "permissions": ["activeTab"]
  1. 完善代码

用户点击扩展程序操作后,检查当前标签页的状态并设置下一个状态。并注入 scripting 脚本,移除页面中所有的图片。

javascript 复制代码
    chrome.runtime.onInstalled.addListener(() => {
    chrome.action.setBadgeText({
      text: "OFF",
    });
  });

chrome.action.onClicked.addListener( (tab) => {
    const prevState = await chrome.action.getBadgeText({ tabId: tab.id });
    const nextState = prevState === 'ON' ? 'OFF' : 'ON'
    await chrome.action.setBadgeText({
      tabId: tab.id,
      text: nextState,
    });

     // 如果状态为 'ON',则移除页面中的所有图片  
     if (nextState === 'ON') {  
      try {  
          // 在当前 tab 中执行脚本以移除所有图片  
          await chrome.scripting.executeScript({  
              target: { tabId: tab.id },  
              func: () => {  
                  // 使用 JavaScript 代码移除页面中的所有 <img> 元素  
                  const images = document.querySelectorAll('img');  
                  images.forEach(img => img.remove());  
              }  
          });  
      } catch (error) {  
          console.error('Error executing script:', error);  
      }  
  }  
})

上述代码中,我们利用 Scripting API 向当前标签页注入脚本,因此也需要在清单中声明 scripting 权限。

json 复制代码
  "permissions": ["activeTab","scripting"]
  1. 加载扩展程序。
  2. 打开一个有图片的网页,点击扩展按钮。触发程序。

使用 Service Worker 处理事件

概览

上面的几个Demo中,我们都有用到 Service Worker 去监听不同的时候,本次教大家如何注册 Service Worker 并导入模块。

构建

  1. 注册 Service Worker。

在项目的根目录中创建manifest.json文件,并添加以下代码:

css 复制代码
{
  "manifest_version": 3,
  "name": "Open extension API reference",
  "version": "1.0.0",
  "icons": {
    "16": "images/icon-16.png",
    "128": "images/icon-128.png"
  },
  "background": {
    "service_worker": "service-worker.js",
  },
}

代码中,扩展程序在清单中注册其 Service Worker,而清单中只接受一个 JavaScript 文件。

  1. 导入多个 Service Worker 模块。

我们的 Service Worker 实现了两项功能。为了提高可维护性,我们将在单独的模块中实现每项功能。首先,我们需要在清单中将 Service Worker 声明为一个 ES 模块,这样我们就可以将模块导入到 Service Worker 中:

css 复制代码
{
 "background": {
    "service_worker": "service-worker.js",
    "type": "module"
  },
}

创建 service-worker.js 文件

arduino 复制代码
import './sw-omnibox.js';
import './sw-tips.js';

分别创建这两个 js 文件。

sw-omnibox.js

arduino 复制代码
console.log("sw-omnibox.js")

sw-tips.js

arduino 复制代码
console.log("sw-tips.js")
  1. 加载扩展程序。
  2. 打开 Service Worker。

在 service-worker.js 中,我们使用了 import 导入了多个模块。它们的执行顺序是根据你在代码中放置 import 语句的顺序来确定的。首先,'./sw-omnibox.js' 模块会被导入并执行,然后 './sw-tips.js' 模块会被导入并执行。

管理标签页

概览

此教程会构建一个标签页管理器,用于整理 Chrome 扩展程序和 Chrome 应用商店的文档标签页。

构建

  1. 创建 manifest.json 文件。
json 复制代码
{
  "manifest_version": 3,
  "name": "Tab Manager for Chrome Dev Docs",
  "version": "1.0"
}
  1. 创建弹出式窗口并设置样式
json 复制代码
{
  ...
  "action": {
    "default_popup": "popup.html"
  },
  ...
}

弹出式窗口与网页类似,但有一点例外:它无法运行内嵌 JavaScript。创建一个 popup.html 文件,并添加以下代码。

xml 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="./popup.css" />
  </head>
  <body>
    <template id="li_template">
      <li>
        <a>
          <h3 class="title">Tab Title</h3>
          <p class="pathname">Tab Pathname</p>
        </a>
      </li>
    </template>

    <h1>Google Dev Docs</h1>
    <button>Group Tabs</button>
    <ul></ul>

    <script src="./popup.js" type="module"></script>
  </body>
</html>

接下来,设置弹出式窗口的样式。创建一个 popup.css 文件,并添加以下代码。

css 复制代码
body {
  width: 20rem;
}

ul {
  list-style-type: none;
  padding-inline-start: 0;
  margin: 1rem 0;
}

li {
  padding: 0.25rem;
}
li:nth-child(odd) {
  background: #80808030;
}
li:nth-child(even) {
  background: #ffffff;
}

h3,
p {
  margin: 0;
}
  1. 管理标签页

通过 Tabs API,扩展程序可以在浏览器中创建、查询、修改和重新排列标签页。Tabs API 中的许多方法无需请求任何权限即可使用。不过,这里我们需要访问标签页的 titleURL;这些敏感属性需要权限。我们可以请求 "tabs" 权限,但这样做会获得对所有标签页的敏感属性的访问权限。由于我们只管理特定网站的标签页,因此会请求较小的主机权限。缩小主机权限范围可让我们向特定网站 授予更高的权限,从而保护用户隐私。这将授予对 titleURL 属性的访问权限以及其他功能。将突出显示的代码添加到 manifest.json 文件中:

json 复制代码
{
  ...
  "host_permissions": [
    "https://developer.chrome.com/*"
  ],
  ...
}

host_permissions 在之前的内容中没有提到,它表示主机权限。专门用于声明扩展程序需要与哪些外部网站或服务进行交互。例如,如果你希望你的扩展可以读取或修改https://developer.chrome.com/上的内容,你就需要在host_permissions中声明这个URL。

host_permissions是关于扩展可以与哪些网站交互的权限,而permissions是关于扩展可以使用哪些Chrome API或功能的权限。

查询标签

我们可以使用 tabs.query() 方法从特定网址检索标签页。创建一个 popup.js 文件并添加以下代码:

csharp 复制代码
const tabs = await chrome.tabs.query({
  url: [
    "https://developer.chrome.com/docs/webstore/*",
    "https://developer.chrome.com/docs/extensions/*",
  ],
});
...

将焦点移至某个标签页

首先,该扩展程序会按字母顺序对标签页名称(所含 HTML 网页的标题)进行排序。然后,当用户点击某个列表项时,它会使用 tabs.update() 将焦点置于该标签页上,并使用 windows.update() 将窗口置于最前面。将以下代码添加到 popup.js 文件中

dart 复制代码
...
const collator = new Intl.Collator();
tabs.sort((a, b) => collator.compare(a.title, b.title));

const template = document.getElementById("li_template");
const elements = new Set();
for (const tab of tabs) {
  const element = template.content.firstElementChild.cloneNode(true);

  const title = tab.title.split("-")[0].trim();
  const pathname = new URL(tab.url).pathname.slice("/docs".length);

  element.querySelector(".title").textContent = title;
  element.querySelector(".pathname").textContent = pathname;
  element.querySelector("a").addEventListener("click", async () => {
    // need to focus window as well as the active tab
    await chrome.tabs.update(tab.id, { active: true });
    await chrome.windows.update(tab.windowId, { focused: true });
  });

  elements.add(element);
}
document.querySelector("ul").append(...elements);
...

为标签分组

TabGroups API 允许扩展程序为组命名并选择背景颜色。通过添加突出显示的代码,向清单中添加 "tabGroups" 权限:

json 复制代码
{
  ...
  "permissions": [
    "tabGroups"
  ]
}

popup.js 中,添加以下代码以创建按钮,该按钮将使用 tabs.group() 对所有标签页进行分组,并将其移动到当前窗口中。

javascript 复制代码
...
const button = document.querySelector("button");
button.addEventListener("click", async () => {
  const tabIds = tabs.map(({ id }) => id);
  const group = await chrome.tabs.group({ tabIds });
  await chrome.tabGroups.update(group, { title: "DOCS" });
});

完整代码

ini 复制代码
const tabs = await chrome.tabs.query({
  url: [
    "https://developer.chrome.com/docs/webstore/*",
    "https://developer.chrome.com/docs/extensions/*",
  ],
});

const collator = new Intl.Collator();
tabs.sort((a, b) => collator.compare(a.title, b.title));
const template = document.getElementById("li_template");
const elements = new Set();
for (const tab of tabs) {
  const element = template.content.firstElementChild.cloneNode(true);

  const title = tab.title.split("-")[0].trim();
  const pathname = new URL(tab.url).pathname.slice("/docs".length);

  element.querySelector(".title").textContent = title;
  element.querySelector(".pathname").textContent = pathname;
  element.querySelector("a").addEventListener("click", async () => {
    // need to focus window as well as the active tab
    await chrome.tabs.update(tab.id, { active: true });
    await chrome.windows.update(tab.windowId, { focused: true });
  });

  elements.add(element);
}
document.querySelector("ul").append(...elements);
const button = document.querySelector("button");
button.addEventListener("click", async () => {
  const tabIds = tabs.map(({ id }) => id);
  const group = await chrome.tabs.group({ tabIds });
  await chrome.tabGroups.update(group, { title: "DOCS" });
});
  1. 加载扩展程序。

  2. 打开下面的网页。

    1. developer.chrome.com/docs/extens...
    2. developer.chrome.com/docs/websto...
    3. developer.chrome.com/docs/extens...
    4. developer.chrome.com/docs/extens...
  3. 点击扩展程序。我们可以在弹出的界面中看到我们打开的页签,并已经按照标题字母顺序排完序。当你点击 Group Tabs 按钮时,会将当前这些页签进行分组。

相关推荐
虚拟搬运工6 小时前
Python类及元类的创建流程
开发语言·chrome·python
drebander11 小时前
ubuntu 安装 chrome 及 版本匹配的 chromedriver
前端·chrome
视觉小鸟2 天前
【JVM安装MinIO】
前端·jvm·chrome
wellshake2 天前
在 Docker 中部署无头 Chrome:在 Browserless 中运行
chrome·docker·容器
王辉辉的猫3 天前
026.(娱乐)魔改浏览器-任务栏图标右上角加提示徽章
chrome
IT-陈3 天前
app抓包 chrome://inspect/#devices
前端·chrome
ForRunner1233 天前
2024 年最佳 Chrome 验证码扩展,解决 reCAPTCHA 问题
前端·chrome
JNU freshman3 天前
Chrome谷歌浏览器登录账号next无反应
前端·chrome
老K(郭云开)3 天前
汉王手写签批控件如何在谷歌、火狐、Edge等浏览器使用
前端·chrome·中间件·edge·创业创新·html5
守城小轩5 天前
Brave编译指南2024 Windows篇:拉取Brave源码(六)
chrome·chrome devtools·指纹浏览器·浏览器开发·brave