概述

当您看完该页面以及入门教程后,您已经准备好开始编写扩展程序了。

基础

扩展程序由一些文件(包括 HTML、CSS、JavaScript、图片以及其他任何您需要的文件)经过 zip 打包得到,为 Google Chrome 浏览器增加功能。扩展程序本质上是网页,它们可以利用浏览器为网页提供的所有 API,例如 XMLHttpRequest、JSON、HTML 5 等等。

扩展程序可以通过内容脚本跨站 XMLHttpRequest 与网页或者服务器交互,扩展程序也可以以编程方式与浏览器功能(例如书签标签页交互)。

扩展程序的用户界面

许多扩展程序(但不包括 Chrome 应用)以浏览器按钮页面按钮的形式向 Google Chrome 浏览器增加用户界面,每个扩展程序最多能有一个浏览器按钮或页面按钮。当扩展程序与大部分网页相关时选择使用浏览器按钮,当扩展程序的图标显示还是消失取决于具体网页时选择使用页面按钮

screenshot screenshot screenshot
这一Google Mail Checker 扩展程序使用浏览器按钮(工具栏中的图标)。 这一Mappy 扩展程序使用页面按钮(地址栏中的图标)以及内容脚本(插入网页的代码)。 单击时显示弹出菜单的浏览器按钮是这一新闻阅读器扩展程序的特色。

扩展程序(以及 Chrome 应用)也可以以其他形式呈现用户界面,例如在 Chrome 浏览器的右键菜单中添加内容,提供选项页面,或者利用内容脚本更改页面的显示方式。有关完整的扩展程序功能以及每一种功能的实现细节,请参见开发者指南

文件

每一个扩展程序包含以下文件:

  • 一个清单文件
  • 一个或多个 HTML 文件(除非扩展程序是一个主题背景)
  • 可选:一个或多个 JavaScript 文件
  • 可选:您的扩展程序需要的任何其他文件,例如图片

当您编写您的扩展程序时,您将所有这些文件放在一个文件夹中。当您发布您的扩展程序时,该文件夹的内容将被压缩成一个特殊的 ZIP 文件,以 .crx 为后缀。如果您通过 Chrome 开发者信息中心上传您的扩展程序,该 .crx 文件会自动为您创建。有关发布扩展的细节,请参见托管

引用文件

您可以在扩展程序中放置您需要的任何文件,但是您如何使用它们呢?通常,您可以通过相对 URL 引用文件,就像在普通的 HTML 页面中那样。下面是一个例子,引用位于子文件夹 images 中名为 myimage.png 的文件。

<img src="images/myimage.png">

您使用 Google Chrome 浏览器的调试器时可能会发现,扩展程序中的每一个文件也可以通过绝对 URL 访问,如下所示:

chrome-extension://<扩展程序标识符>/<文件路径>

在这一 URL 中,<扩展程序标识符> 是扩展程序系统为每一个扩展程序生成的唯一标识符,您可以进入 chrome://extensions 查看您加载的所有扩展程序的标识符。<文件路径> 是扩展程序的主目录下的文件位置,与相对 URL 相同。

当您编写扩展程序时(打包之前),扩展程序标识符可能会更改。特别地,如果您从另一个目录加载未打包的扩展程序,它的标识符会改变,当您打包扩展程序时会再次改变。如果您的扩展程序代码需要指定扩展程序中某个文件的完整路径,您可以使用 @@extension_id 这一预定义消息,避免开发过程中硬编码标识符。

当您打包扩展程序时(典型情况下,通过信息中心上传),该扩展程序将会获得一个永久的标识符,即使您更新了这一扩展程序仍然保持不变。一旦扩展程序的标识符变成永久的了,您就可以将所有的 @@extension_id 修改为真正的唯一标识符。

清单文件

清单文件名为 manifest.json ,提供有关扩展程序的各种信息,例如最重要的文件和扩展程序可能具有的能力。以下是一个典型的清单文件,用于一个浏览器按钮,它将会访问来自 google.com 的信息:

{
  "name": "我的扩展程序",
  "version": "2.1",
  "description": "从 Google 获取信息。",
  "icons": { "128": "icon_128.png" },
  "background": {
    "persistent": false,
    "scripts": ["bg.js"]
  },
  "permissions": ["http://*.google.com/", "https://*.google.com/"],
  "browser_action": {
    "default_title": "",
    "default_icon": "icon_19.png",
    "default_popup": "popup.html"
  }
}

有关更多细节,请参见清单文件

架构

许多扩展程序有一个后台网页,它是一个包含扩展程序主要逻辑的不可见页面。扩展程序也可以包含其他页面,展现扩展程序的用户界面。如果扩展程序需要与用户加载的网页交互(相对于包含在扩展程序中的页面),扩展程序必须使用内容脚本。

后台网页

下图所示的浏览器至少安装了两个扩展程序:一个浏览器按钮(黄色图标)和一个页面按钮(蓝色图标)。浏览器按钮和页面按钮都有后台页面。下图显示了浏览器按钮的后台页面,由 background.html 定义,并且包含在这两个窗口中控制浏览器按钮的 JavaScript 代码。

Two windows and a box representing a background page (background.html). One window has a yellow icon; the other has both a yellow icon and a blue icon. The yellow icons are connected to the background page.

后台网页分两种:持续运行的后台网页事件页面。正如它们的名称所述,持续运行的后台网页保持打开状态,事件页面根据需要打开与关闭。除非您绝对需要您的后台网页一直运行,请首选事件页面。

有关更多细节,请参见事件页面后台网页

用户界面网页

扩展程序可以包含普通的 HTML 网页,用来显示扩展程序的用户界面。例如,浏览器按钮可以包含弹出菜单,通过 HTML 文件实现。任何一个扩展程序都可以有选项页面,让用户自定义扩展程序的工作方式。另外一种特殊页面是替代页面。最后,您可以使用 tabs.createwindow.open() 来显示扩展程序中的任何其他 HTML 文件。

扩展程序中的 HTML 网页可以互相访问其他页面的全部 DOM,并且可以互相调用函数。

下图显示了浏览器按钮弹出菜单的架构。弹出菜单是由一个 HTML 文件(popup.html)定义的网页,该扩展程序也正好有一个后台网页(background.html)。弹出窗口不用重复后台网页中的代码,因为弹出窗口可以调用后台网页上的函数。

A browser window containing a browser action that's displaying a popup. The popup's HTML file (popup.html) can communicate with the extension's background page (background.html).

有关更多细节,请参见浏览器按钮选项替代页面页面间通信这些部分。

内容脚本

如果您的扩展程序需要与网页交互,您就需要使用内容脚本。内容脚本是一些 JavaScript 代码,它们在浏览器中已加载页面的上下文中执行。您应该将内容脚本视为已加载页面的一部分,而不是打包在一起的扩展程序(它所属的扩展程序)的一部分。

内容脚本可以读取浏览器访问的网页的细节,并且可以修改页面。在下图中,内容脚本可以读取并且修改显示的网页的 DOM。然而,它不能修改所属扩展程序后台网页的 DOM。

A browser window with a browser action (controlled by background.html) and a content script (controlled by contentscript.js).

内容脚本并不是完全与所属扩展程序隔离的。内容脚本可以与所属扩展程序交换消息,如下图箭头所示。例如,每当在浏览器页面中发现 RSS 供稿时,内容脚本可以发送消息,反过来后台页面也可以发送消息要求内容脚本更改浏览器页面的外观。

Like the previous figure, but showing more of the parent extension's files, as well as a communication path between the content script and the parent extension.

有关更多信息,请参见内容脚本

使用 chrome.* API

扩展程序除了能够使用网页和应用可以使用的所有 API 外,还能使用仅用于 Chrome 浏览器的 API(通常称为 chrome.* API)来更好地与浏览器集成。例如,任何扩展程序或网上应用可以使用标准的 window.open() 方法来打开一个网页,但是如果您想指定网页应该显示在哪个窗口中,您的扩展程序就可以使用仅用于 Chrome 浏览器的 tabs.create 方法。

异步方法与同步方法的区别

大部分 chrome.* API 的方法都是异步的,它们不等待操作完成就立即返回。如果您需要知道操作结果,您可以向方法传递一个回调函数,回调函数将稍后在方法返回后的某个时刻执行(可能很久之后)。下面是一个异步方法签名的例子:

chrome.tabs.create(object createProperties, function callback)

也有一些 chrome.* 方法是同步的。同步的方法没有回调参数,因为它们只有当所有操作完成后才返回。通常,同步方法有返回值类型。考虑 runtime.getURL 方法:

string chrome.runtime.getURL()

该方法没有回调参数,但是有返回值类型 string,因为它同步地返回 URL,不进行任何其他异步操作。

例子:使用回调函数

假设您想在用户当前选定的标签页中打开新的页面。要想这么做,您首先需要获得当前标签页的标识符(使用 tabs.query),然后使该标签转到指定的新的 URL(使用 tabs.update)。

假如 query() 是同步的,您可能会写这样的代码:

//以下代码不能正常工作!
var tab = chrome.tabs.query({'active': true}); //错误!
chrome.tabs.update(tab.id, {url:newUrl});
someOtherFunction();

这样的方法不行,因为 query() 是异步的,它不等待操作完成就返回了,并且事实上它都不返回任何值(尽管有些异步方法会返回信息)。您可以通过它签名中的 callback参数看出 query() 是异步的:

chrome.tabs.query(object queryInfo, function callback)

要改正上面的代码,您必须使用那个回调参数。以下代码显示如何定义一个回调函数,从 query() 获得结果(通过名为 tab 的参数)并调用 update()

//以下代码可以正常工作
chrome.tabs.query({'active': true}, function(tabs) {
  chrome.tabs.update(tabs[0].id, {url: newUrl});
});
someOtherFunction();

在这一例子中,以上几行是按照这样的顺序执行的:1、4、2。只有在有关当前选定标签的信息可用后,即 query() 返回后的某一时刻,才调用在 query() 中指定的回调函数(并且执行第二行)。尽管 update() 是异步的,这一例子没有使用回调参数,因为我们对于调用的结果并不感兴趣。

更多详情

有关更多信息,请参见 chrome.* API 文档并观看以下视频(英文):

页面间的通信

扩展程序中的 HTML 网页通常需要通信。因为一个扩展程序的所有网页在同一个进程中的同一个线程上执行,网页之间可以直接调用函数。

要获得扩展程序中的网页,请使用 chrome.extension 方法,例如 extension.getViewsextension.getBackgroundPage。一旦一个网页引用了扩展程序中的其他网页,第一个网页可以执行其他网页上的函数,并且可以操纵它们的 DOM。

保存数据和隐身模式

扩展程序可以使用 storage API、HTML5 网页存储 API(例如 localStorage)或者向服务器发出请求保存数据。每当您要保存任何数据前,首先要考虑它是否来自隐身窗口。默认情况下,扩展程序不在隐身窗口中运行。当浏览器处于隐身模式时,您需要考虑用户对您的扩展程序的需求。

隐身模式确保不会留下任何痕迹。当处理来自隐身窗口的数据时,尽可能地遵守这一约定。例如,如果您的扩展程序通常将浏览器历史记录保存至云端,不要保存来自隐身窗口的历史记录。另一方面,您可以在任何窗口中保存您的扩展程序设置,无论隐身与否。

准则: 如果某些数据可能显示用户在网上的访问记录或者用户所做的事情,千万不要保存这些来自隐身窗口的数据。

要确定窗口是否处于隐身模式,检查相关的 tabs.Tabwindows.Window 对象的 icognito 属性。例如:

function saveTabData(tab, data) {
  if (tab.incognito) {
    chrome.runtime.getBackgroundPage(function(bgPage) {
      bgPage[tab.url] = data;      // 仅在内存中保留数据
    });
  } else {
    localStorage[tab.url] = data;  // 可以保存数据
  }
}

现在做什么呢?

现在您已经大致了解扩展程序了,您应该准备好编写您自己的扩展程序了。您接下来可以参考以下内容: