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

开发第一个插件

为了让大家更快速、更直观地了解插件开发的入门过程,我们将参考官方的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 按钮时,会将当前这些页签进行分组。

相关推荐
代码的乐趣1 天前
支持selenium的chrome driver更新到133.0.6943.126
chrome·python·selenium
G佳伟1 天前
【亲测有效】百度Ueditor富文本编辑器添加插入视频、视频不显示、和插入视频后二次编辑视频标签不显示,显示成img标签,二次保存视频被替换问题,解决方案
chrome·百度·音视频
微wx笑2 天前
chrome扩展程序如何实现国际化
前端·chrome
CUIYD_19892 天前
Chrome 浏览器(版本号49之后)‌解决跨域问题
前端·chrome
Dontla3 天前
华为昇腾服务器(固件版本查询、驱动版本查询、CANN版本查询)
运维·服务器·chrome
JsenLong3 天前
ubuntu 守护进程
linux·chrome·ubuntu
前端大全3 天前
Chrome 推出全新的 DOM API,彻底革新 DOM 操作!
前端·chrome
林的快手3 天前
CSS文本属性
前端·javascript·css·chrome·node.js·css3·html5
码农君莫笑3 天前
Linux系统上同时打印到物理打印机并生成PDF副本方法研究
linux·前端·chrome·打印·信管通
代码轨迹4 天前
青龙面板运行selenium启动Chrome报错
chrome·python·selenium