概述

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

基础

扩展程序由一些文件(HTML,js,css,图片等其他文件)经过zip打包得到,其本质还是网页,可以使用浏览器网页提供的所有API,如 XMLHttpRequest、JSON、HTML 5 等。

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

扩展程序的用户界面

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

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

文件

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

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

引用文件

可以在扩展程序中放置任何文件,使用的话可以通过相对 URL 引用文件,如: <img src="images/myimage.png">

使用Chrome的调试器可发现,扩展程序中的每一个文件也可以通过绝对 URL 访问,如: chrome-extension://<扩展程序标识符>/<文件路径>

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

清单文件

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

  1. {
  2. "name": "我的扩展程序",
  3. "version": "2.1",
  4. "description": "从 Google 获取信息。",
  5. "icons": { "128": "icon_128.png" },
  6. "background": {
  7. "persistent": false,
  8. "scripts": ["bg.js"]
  9. },
  10. "permissions": ["http://*.google.com/", "https://*.google.com/"],
  11. "browser_action": {
  12. "default_title": "",
  13. "default_icon": "icon_19.png",
  14. "default_popup": "popup.html"
  15. }
  16. }

关于manifest文件的更多细节: manifest.json

架构

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

后台网页

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

概述 - 图1

后台网页分两种:持久运行的后台网页与事件页面。顾名思义,持续运行的后台网页保持打开状态,事件页面根据需要打开与关闭。除非绝对需要你的后台网页一直运行,最好首选事件页面。

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

用户界面网页

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

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

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

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

内容脚本 content_script

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

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

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

更多细节,参考 内容脚本

使用 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() 是同步的,您可能会写这样的代码:

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

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

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

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

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

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

更多

更多信息,参考 chrome.* API 文档和下面的视频(英文):

页面间的通信

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

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

保存数据和隐身模式

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

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

准则: 如果某些数据可能显示用户在网上的访问记录或者用户所做的事情,千万不要保存这些来自隐身窗口的数据。 要确定窗口是否处于隐身模式,检查相关的 tabs.Tab 或 windows.Window 对象的 icognito 属性。例如:

  1. function saveTabData(tab, data) {
  2. if (tab.incognito) {
  3. chrome.runtime.getBackgroundPage(function(bgPage) {
  4. bgPage[tab.url] = data; // 仅在内存中保留数据
  5. });
  6. } else {
  7. localStorage[tab.url] = data; // 可以保存数据
  8. }
  9. }

下一步

  • 教程:入门
  • 教程:调试
  • 开发者指南
  • 示例
  • 视频(英文),例如扩展程序的消息传递