Chrome 拓展基础

完成了入门例子后,继续学习入门例子推荐的后续文章,开始迈向拓展大师的第一步。

原文地址

总览

一旦完成本页面和入门指南,你就可以开始写属于你的拓展了。

基础

拓展是一个文件的压缩包,由 HTML,JavaScript,图片,和其它你需要的文件组成,对 Chrome 浏览器的功能进行拓展。拓展实质上是一个网页,它们可以使用所有浏览器对网页提供的 APIs,从 XMLHttpRequest 到 JSON 到 HTML5。

拓展可以使用 content scripts 或 跨域 XMLHttpRequests 与 web 页面或服务器交互。拓展也可以以编程方式与浏览器功能交互例如书签和标签。

拓展的界面

许多拓展 (非 Chrome 应用程序) 会添加界面到 Google Chrome 中,以 browser actions 方式或 page actions 方式。每个拓展最多可以有一个 browser action 或 page action。当拓展对大部分页面有用时选择 browser action,当拓展对某些页面显示,不显示,依赖时选择 page action。

Google Mail Checker 拓展使用 browser action (图标在工具栏中) Mappy 拓展使用 page action (图标在地址栏中) 和 content script (代码注入网页) New Reader 拓展 拥有一个 browser action,当点击后,显示一个弹出框。

拓展 (和 Chrome 应用程序) 也能以其它方式呈现界面,例如添加到 Chrome 右键菜单,或提供一个选项页面,或使用 content script 更改页面的外观。见 Developer‘s Guide 中完整的拓展功能列表,它会实现每一个的细节。

文件

每一个拓展都有下面的文件:

  • manifest 文件
  • 一个或多个 HTML 文件 (除非拓展是主题)
  • 可选:一个或多个 JavaScript 文件
  • 可选:拓展需要的其它文件-例如,图像文件

当开发拓展时,把这些文件放入一个单独的文件夹。发布拓展时,文件的内容会打包成一个特定后缀 .crx 的ZIP 文件。如果将拓展上传到 Chrome Developer Dashboard,它会为你创建 .crx 文件。发布拓展的细节,见 Hosting。

引用文件

你可以将任意文件链接到拓展中,但实际如何操作呢?通常,可以使用相对 URL 引用文件,就像普通 HTML 页面一样,这里有一个例子引用子目录 images 的 myimage.png 文件。

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

你可能注意到使用 Google Chrome 调试时,拓展的每个文件都可以通过绝对路径访问:

1
chrome-extension://<extensionID>/<pathToFile>

在这个 URL 中, 是系统为每个拓展生成的唯一标识。你可以进入 URL: chrome-extension 看到所有已加载的拓展 ID。 是拓展的顶部文件夹之下的文件位置;如相对 URL。

当开发拓展时 (打包之前),拓展的 ID 可以更改。特别注意,未打包的拓展如果从不同目录加载那么它的 ID 将会改变;当拓展打包后 ID 将会再次改变。如果拓展代码需要指定文件的完整路径,可以使用predefined message @@extension_id 避免在开发期间使用硬编码 ID。

当打包拓展时 (通常,通过将它上传到 dashboard),拓展会获得一个永久 ID,甚至更新拓展后也会保留。一旦拥有永久 ID 后,你可以把所有使用 @@extension_id 的位置替换为真实 ID。

manifest 文件

manifest 文件,名为 manifest.json,提供有关拓展的信息,例如大部分重要文件和拓展用到的功能。这里是一个典型的 manifest 文件,从 google.com 获取信息的 browser action。

1

{
  "name": "My Extension",
  "version": "2.1",
  "description": "Gets information from 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"
  }
}

更多细节,见 Manifest Files。

架构

许多拓展都有一个 background page,一个保持拓展主要逻辑的不可见页面。拓展也可以包含其它呈现拓展界面的页面。如果拓展需要与由用户加载网页 (包含拓展的页面) 交互,那么拓展必须使用 content script。

background page

下面的图展示了至少安装了两个拓展的浏览器:一个 browser action (黄色图标) 和 page action (蓝色图标)。browser action 和 page action 都有 background page。这个图展示了 browser action 的 background page,在这两个窗口中它由 background.html 定义并拥有控制 browser action 行为的 JavaScript 代码。

这里有两种类型的 background page :persistent background pages,和 event pages。Persistent background pages,如名称所示,总是打开的。Event pages 会在需要时打开或关闭。除非你一定需要 background page 一直运行,否则最好使用 event page。

更多 Event Pages 和 Background Pages 的细节。

UI pages

拓展可以包含显示拓展界面的常规 HTML 页面,例如,browser action 可以弹出窗口,它是通过一个 HTML 文件实现的。任何拓展都可以有一个 options 页面,它可以让用户自定义拓展如何工作。另一个特殊页面类型是从重写页面。最后,你可以使用 tabs.create 或 window.open() 显示任何其它拓展中的 HTML 文件。

拓展内部的 HTML 页面可以互相访问 DOMs,和互相调用它们的函数。

下面的图例展示 browser action 的弹出窗口的架构。弹出的内容定义在一个 HTML 文件中 (popup.html)。这个拓展也有一个 background page (background.html)。弹出窗口不需要复制 background page 的代码因为它可以调用 background page 的函数。

见 Browser Actions,Options,Override Pages,和 Communication between pages 等章节了解更多细节。

Content scripts

如果你的拓展需要与 web 页面交互,那么要用到 content script。content script 是在已经加载到浏览器的网页的上下文中执行的一些 JavaScript 代码。可以认为 content script 是已加载页面的一部分,而不是拓展的一部分 (它的父拓展)。

Content script 可以读取浏览器所访问的网页细节,可以对这个页面更改。在下面的图中,content script 可以读取和修改已显示的网页的 DOM,但它不能修改它的父拓展的 background page 的 DOM。

content script 不完全与它的父拓展切断关系,content script 可以与它的父拓展交换信息,如图中显示的箭头。例如,content script 可以发送一个是否在浏览器页面找到 RSS 订阅的信息。或 background page 可以发送信息要求 content script 更改浏览器页面的外观。

更多信息,见 Content Script。

使用 chrome.* APIs

除了可以访问所有网页和可用的应用程序的 APIs,拓展也可以使用与浏览器紧密关联的只在 Chrome 使用的 API。例如,任何拓展或 web 应用程序可以使用标准的 window.open() 方法打开 URL。但如果你想在指定的窗口中打开,拓展只能在 Chrome 使用 tabs.create 方法。

异步 vs 同步方法

大多数 chrome.APIs 方法是*异步立即返回的,不需要等待操作完成。如果你需要获得操作的结果,你可以传入回调函数到方法中。回调会在不久后执行 (可能很久),至少会在方法返回之后。这里是一个异步方法的签名:

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

其它 chrome. 方法是*同步的,同步方法永远不需要回调函数因为它们直到任务完成后才会返回。通常,同步方法有一个返回类型。如 runtime.getURL 方法:

string chrome.runtime.getURL()

这个方法没有回调并返回 string 类型因为它同步返回 URL 且不执行其他异步的工作。

例子:使用回调

如果要将用户当前选择的标签导航到新的 URL,你需要获取当前标签的 ID (使用 tabs.query) 然后使标签进入新的 URL (使用 tabs.update)。

如果 query() 是同步的,你可以写这样的代码:

1

//THIS CODE DOESN'T WORK
var tab = chrome.tabs.query({'active': true}); //WRONG!!!
chrome.tabs.update(tab.id, {url:newUrl});
someOtherFunction();

但这是错误的因为 query() 是异步的。它直接返回不会等待工作完成,它甚至不会返回值 (尽管一些异步方法可以)。你可以从方法的签名中的 callback 参数知道 query() 是异步的。

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

要修复上面的代码,你必须使用回调参数。下面的代码展示如何使用回调方法从 query() 获取结果 (有一个名为 tabs 的参数) 并调用 update()。

1

//THIS CODE WORKS
chrome.tabs.query({'active': true}, function(tabs) {
  chrome.tabs.update(tabs[0].id, {url: newUrl});
});
someOtherFunction();

在这个例子中,行号按这个顺序执行:1,4,2。指定到 query() 的回调函数只有在当前标签是活动的才调用,这个时间在 query() 返回之后。尽管 update() 是异步的,但这个例子中未使用回调参数,因为我们不需要任何有关更新的结果。

更多细节

更多信息,见 chrome.* API docs 和观看视频。。。

页面之间通信

HTML 页面与拓展通常都需要通信。因为所有拓展的页面在同一个线程同一个处理中执行,页面彼此之间可以直接调用函数。

要在拓展中寻找页面,使用 chrome.extension 方法例如 getViews() 和 getBackgroundPage()。一旦页面引用到拓展内其它页面,第一个页面可以调用其它页面的函数,和修改它们的 DOMs。

保存数据和匿名模式

拓展可以使用 API storage 保存数据,HTML 5 web storage API (例如 localStorage) 或通过向服务器投递请求保存数据。每当你想保存时,首先要考虑窗口是否处于匿名模式。默认情况,拓展不会运行于匿名窗口。你需要考虑当浏览器是匿名模式时用户期望拓展执行什么。

匿名模式 承诺不会保留窗口踪迹。当处理匿名窗口的数据时,你最好尊循这个约定。例如,如果你的拓展通常将浏览历史保存在云中,但不要保存匿名窗口的历史纪录。在其它的处理中,你可以在任意窗口或匿名窗口保存拓展的设置,或不保存。

经验之谈:如果数据是说明用户访问了什么网页,或完成了什么操作的信息,那么不要在匿名窗口保存它们。

要判断窗口是否匿名模式,检测 tabs.Tab 的 incognito 属性或 windows.Window 对象。例如:

1

function saveTabData(tab, data) {
  if (tab.incognito) {
    chrome.runtime.getBackgroundPage(function(bgPage) {
      bgPage[tab.url] = data;      // Persist data ONLY in memory
    });
  } else {
    localStorage[tab.url] = data;  // OK to store data
  }
}

后续

现在已经完成拓展的介绍,你应该开始写一个属于你的拓展。这里是一些下一步的建议: