一步步教你如何制作 Chrome 扩展程序

2 阅读7分钟

原文链接:How to Make a Chrome Extension: Step-by-Step Guide,2024.02.03,by Hardeep Asrani。翻译时有删改。

本文我将带领大家制作一个使用浏览器弹出菜单发送浏览器通知的 Chrome 扩展程序。中间会涉及上下文菜单(context menu)和数据存储 API(data storage API)的使用,暂且叫它 Notify

image.png

本扩展程序的代码已放到 Github 上,你可以随意 Fork 并测试。

项目结构

在开始之前,你可以查看 Google Chrome 关于 Chrome 扩展工具的开发人员文档,对扩展程序有一个大致的了解。

另外,如果需要将扩展程序发不到 Chrome Web Store,那么可以阅读下关于单一用途政策(single-purpose policy)的介绍。

好了,说了这么多,现在开始我们这一次扩展程序的编写吧。

首先,常见一个名为 notify 的文件夹,存放扩展程序所有相关资源和代码。

接下来,在创建一个清单文件,名称固定为 manifest.json,包含扩展程序的一些配置信息。

{
  "name": "Notify!",
  "description": "A Google Chrome extension!",
  "version": "1.0",
  "manifest_version": 3,
  "icons": {
    "48": "/assets/icons/48.png",
    "128": "/assets/icons/128.png"
  }
}

这里包含了关于我们这个扩展程序的相关元信息。例如:名称、描述和版本号。另外,只能在扩展程序中使用的 Chrome Extensions API 也是有版本号的,使用 manifest_version 字段指定,目前最新的版本是 3。

加载扩展程序

准备好清单文件后,就可以在 Chrome 浏览器中加载扩展程序了:

image.png

地址栏导航到 chrome://extensions,打开扩展程序管理界面。当然也可以从点击最右侧设置图标通过“扩展程序”菜单进入到管理界面。

接下来,启用开发人员模式并点击最左侧按钮加载我们未被打包的原始扩展程序目录 notify。

然后,就能看到扩展程序被成功加载了,后面我们会在逐步的修改中观察它的变化。

注意,请确保扩展程序的图标加入到 assets/icons 文件夹下了,否则展示的将会是默认图标。

添加弹窗界面

现在,我们为扩展程序添加弹出界面,用户可以使用我们提供的选项进行交互。

有很多方法实现这一点,例如添加整个页面,不过弹出窗口通常是目前大多数扩展程序采用的方式。

要向扩展程序添加弹出窗口,需要向 manifest.json 添加一些额外的配置项。

{
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "48": "/assets/icons/48.png",
      "128": "/assets/icons/128.png"
    }
  },
}

这个配置设置了扩展程序的弹出窗口所在的 HTML 以及默认图标。通过 default_icon 设置的是默认图标,这个图标是可以通过 API 修改的。

Google Chrome 扩展程序的弹出窗口

以下是我们的弹出窗口文件 popup.html 代码。

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="assets/css/popup.css">
  </head>
  <body>
    <div id="notify-wrapper">
      <div id="notify-header">
        <h1>Notify!</h1>
      </div>
      <div id="notify-containers">
        <div class="notify-form">
          <label for="text">Notification</label>
          <textarea name="text" id="notify-text" placeholder="Let's notify!"></textarea>
        </div>
        <div class=notify-buttons>
          <p>Total: <span id="notify-count"></span></p>

          <button class="button" id="notify-reset">
            Reset
          </button>
          <button class="button primary" id="notify-button">
            Notify!
          </button>
        </div>
      </div>
    </div>
    <script src="assets/js/popup.js"></script>
  </body>
</html>

这个 HTML 文件还引入了样式表和脚本,来为弹出窗口添加样式和功能。

你可以在这里获取 CSS 文件,而 JavaScript 文件中的内容会在后续过程中逐步实现。

到目前为止,我们已经创建了一个带有弹出窗口的 Google Chrome 扩展程序,而者只需要编写几行代码就能实现。

下面,我们将继续向这个扩展添加功能。

使用通知 API

顾名思义,我们要做的是一个具备通知功能的扩展程序,现在就着手实现吧。

在使用通知 API 之前,我们首先需要在 manifest.json 文件中为使用通知功能申请权限。这样做的原因之一是让你的用户在安装之前知道这个扩展程序会需要哪些权限。

对通知 API 来说,我们要这样写。

{
  "permissions": [
    "notifications"
  ],
}

另外,popup.js 中是无法直接使用通知 API 的,要用到 Service Worker 来发送通知,因此还要在清单文件中设置:

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

background.js 文件内容如下。

chrome.runtime.onMessage.addListener( data => {
  if ( data.type === 'notification' ) {
          chrome.notifications.create(
              '',
              {
                  type: 'basic',
                  title: 'Notify!',
                  message: data.message || 'Notify!',
                  iconUrl: './assets/icons/128.png',
              }
          );
  }
});

此处,我们通过 chrome.runtime.onMessage 增加事件侦听器,来获取发送通知的指令,这个指令是从 popup.js 文件发出的,接下来会介绍。

chrome.notifications.create() API 就是用来创建通知窗口的。这里我们只使用了基础版本(type: 'basic')的通知窗口,其他可用选择可以参考这里。

现在,创建通知的逻辑有了。接下来添加 popup.js 文件,触发通知指令。

const text = document.getElementById( 'notify-text' );
const notify = document.getElementById( 'notify-button' );

notify.addEventListener( 'click', () => {
  chrome.runtime.sendMessage( '', {
    type: 'notification',
    message: text.value
  });
} );

点击弹出窗口的 Notify 按钮,就会通过 chrome.runtime.sendMessag() 发送一个通知指令了。这样,我们就有了一个可以触发通知的扩展程序了。

image.png

注意:确保你使用的是最新版本的 Google Chrome 浏览器,并已授予其触发通知的权限,否则通知不会出现。

添加上下文菜单

所谓“上下文菜单(context menu)”就是页面中右键单击时出现的菜单:

image.png

需要上下文菜单的场景很多。一个比较场景的例子就是通常光标选择文本后,使用 Google 进行搜索。

我们可以需要向上下文菜单添加任意数量的菜单项,但如果你的扩展程序添加了多个项目,它们会折叠在一个父菜单项下。

跟通知功能一样,我们需要在清单文件申明权限:

{
  "permissions": [
    "contextMenus",
    "notifications"
  ],
}

contextMenus 表示菜单权限,现在就可以在 background.js 中使用了。

chrome.runtime.onMessage.addListener( data => {
  if ( data.type === 'notification' ) {
    notify( data.message );
  }
});

chrome.runtime.onInstalled.addListener( () => {
  chrome.contextMenus.create({
    id: 'notify',
    title: "Notify!: %s", 
    contexts:[ "selection" ]
  });
});

chrome.contextMenus.onClicked.addListener( ( info, tab ) => {
  if ( 'notify' === info.menuItemId ) {
    notify( info.selectionText );
  }
} );

const notify = message => {
  return chrome.notifications.create(
    '',
    {
      type: 'basic',
      title: 'Notify!',
      message: message || 'Notify!',
      iconUrl: './assets/icons/128.png',
    }
  );
};

为了代码复用,我们将创建通知的代码抽象在了 notify() 函数中。

contextMenus.create() API 用于将向向上下文菜单添加菜单项,写在 onInstalled 钩子函数中,表示只需要初始化一次。

之后,与上一步类似,我们使用 contextMenus.onClicked 捕获上下文菜单项的点击,如果点击是我们注册的菜单项(id: 'notify'),就触发通知。

image.png

这是捆绑扩展程序功能的一种非常漂亮的方法。如果你留意你使用的浏览器上使用的其他扩展程序,会发现许多都利用这个空间来增强其扩展程序的体验。

使用存储 API 存储数据

以上,我们已经做出来了一个具备基本功能的扩展程序,再来看看存储 API(Storage API)的使用。当你想将一些用户数据存储到扩展程序中的时候,存储 API 就很有用。

存储 API 分 2 种:本地(local)和同步(sync)。顾名思义,本地存储将你的数据保存在浏览器本地。相比之下,同步存储则允许将数据在同一 Google 帐户中同步。

依然,你需要先在清单文件申请存储权限:

"permissions": [
  "contextMenus",
  "notifications",
  "storage"
],

之后,便可以使用 storage.local.get()、storage.local.set() 方法来查询和保存数据了。

接下来,向 popup.js 文件中添加如下代码。

const reset = document.getElementById( 'notify-reset' );
const counter = document.getElementById( 'notify-count' );

chrome.storage.local.get( ['notifyCount'], data => {
  let value = data.notifyCount || 0;
  counter.innerHTML = value;
} );

chrome.storage.onChanged.addListener( ( changes, namespace ) => {
  if ( changes.notifyCount ) {
    let value = changes.notifyCount.newValue || 0;
    counter.innerHTML = value;
  }
});

reset.addEventListener( 'click', () => {
  chrome.storage.local.clear();
  text.value = '';
} );

这段代码做了两件事:

  • 当我们打开弹出窗口或存储的值发生变化时,会更新弹出窗口中的计数。监听存储变化使用的是 storage.onChanged
  • 另外,当用户单击重置按钮时,我们会清除存储

接下来再修改 background.js 中的 notify() 函数,增加计数支持。

const notify = message => {
  chrome.storage.local.get( ['notifyCount'], data => {
    let value = data.notifyCount || 0;
    chrome.storage.local.set({ 'notifyCount': Number( value ) + 1 });
  } );

  return chrome.notifications.create(
    '',
    {
      type: 'basic',
      title: 'Notify!',
      message: message || 'Notify!',
      iconUrl: './assets/icons/128.png',
    }
  );
};

这里,我们获取最新的存储数据,并更新它。

同样,你也可以使用 chrome.storage.sync API 在浏览器之间同步选项。

总结

以上,我们就完成了一个 Google Chrome 扩展程序,该扩展程序使用了 Google Chrome 的很多不同功能。

  • Popups:弹出窗口
  • Notifications API:通知 API
  • Context Menu:上下文菜单
  • Storage API:存储 API

你可以在此 Notify 的 GitHub 仓库中获取完整代码,也可以将它发布到 Chrome Web Store,供其他用户下载使用。

如果你想探索 Google Chrome 为开发人员提供的其他能力,我建议你查看官方文档