一、描述

扩展程序是有 HTML、CSS和JavaScript 以及 Images 和其他文件组成的压缩 bundle,目的是为了提高 chrome 的浏览器体验。

扩展程序是通过 Web 技术构建的,可以使用浏览器提供给开放 Web 的相同 API。

扩展程序有很多功能可以去做,能够修改用户查看和交互的 Web 内容,或者扩展和更改浏览器本身的行为。甚至能够扩展网关,是 chrome 成为个性化的浏览器。

二、扩展程序的文件

扩展名的文件类型和目录数量各不相同,但是它们都需要一个 manifest.json,一些基本的扩展程序可能只包括 manifest 和 工具栏的图标。

manifest.json 给浏览器提供扩展程序的相关信息,比如重要的文件和扩展程序可能使用的功能或者权限。

{
    "name": "My Extension",
    "version": "2.1",
    "description": "Gets information from Google.",
    "icons": {
      "128": "icon_16.png",
      "128": "icon_32.png",
      "128": "icon_48.png",
      "128": "icon_128.png"
    },
    "background": {
      "persistent": false,
      "scripts": ["background_script.js"]
    },
    "permissions": ["https://*.google.com/", "activeTab"],
    "browser_action": {
      "default_icon": "icon_16.png",
      "default_popup": "popup.html"
    }
  }

扩展程序会有一个在工具栏上的图标,工具栏图标能够轻松的访问我们的扩展程序,并且用户也知道安装了哪些扩展,大多数用户都是通过单机图标和使用弹出的窗口进行交互的。

1.jpg

文件引用

一个扩展程序可以使用相对 URL 引用相关的文件,就像是 HTML 页面去引用一样。

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

当然,除了相对路径,也可以使用绝对路径去引用,一般绝对路径长下面这个样子:

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

在绝对URL中,<extensionID>是扩展系统为每个扩展生成的唯一标识符,可以通过转到URL chrome:// extensions 来查看所有已加载扩展程序的 ID。 <pathToFile>是扩展顶级文件夹下文件的位置; 它匹配相对 URL。

2.jpg

如果你是本地开发,是通过加载 已解压程序文件 方式打开扩展程序,这个 id 可能会变化,具体来说,如果从不同的目录名加载扩展程序,这个扩展程序的 id 会发生变化,打包的时候, id 会再次变化。

如果扩展程序的代码依赖绝对 URL,可以使用 chrome.runtime.getURL() 来获取绝对URL,这样子可以避免硬编码。

三、体系结构

之前就已经说过了,扩展程序是由多个组件组成的,大致有如下:

  • Manifest
  • Background Script
  • UI Elements
  • Content Script
  • Options Page

Background Script

后台脚本是扩展的事件处理程序 ;它包含对扩展程序非常重要的浏览器事件的监听器。

本身 后台脚本 是处于休眠状态,直到事件被触发然后执行指令逻辑。

有效的后台脚本仅在需要时加载,在空闲时卸载。

UI Elements

扩展程序的用户界面原则应该是有目的和最小化的。

用户界面应该自定义或增强浏览体验,而不会分散注意力。

大多数扩展都有浏览器操作或页面操作,但可以包含其他形式的 UI,例如上下文菜单,多功能框的使用或键盘快捷键的创建。

扩展程序的 UI 页面(例如弹出窗口 popup)可以包含具有 JavaScript 逻辑的普通 HTML 页面。扩展程序也可以调用 tabs.createwindow.open() 来显示扩展中存在的其他 HTML 文件。

使用页面操作和弹出窗口的扩展程序可以使用声明性内容 API 在后台脚本中设置规则,以便用户可以使用弹出窗口。满足条件时,后台脚本与弹出窗口通信,使其图标可以单击给用户,而在平时,这个图标是灰色无作用的。

交互流程如下所示:

3.png

Content scripts

读取或写入网页的扩展程序可以通过内容脚本。内容脚本包含在已加载到浏览器中的页面的上下文中执行的 JavaScript 。

内容脚本能够读取和修改浏览器访问的网页的 DOM。

4.png

内容脚本可以通过使用存储 API 交换消息和存储值来与其父扩展进行通信。

6.png

Options Page

就像扩展程序允许用户自定义Chrome浏览器一样,选项页面也可以自定义扩展程序。选项可用于启用功能,并允许用户选择与其需求相关的功能。

本质上就是一个扩展程序额外的配置页面。

四、使用 chrome API

除了可以访问与网页相同的 API 之外,扩展程序还可以使用特定于扩展程序的 API 来和使用浏览器的一些功能。

扩展程序和网页都可以访问标准的 window.open()方法来打开网址,但扩展程序可以使用 Chrome API tabs.create 方法指定应在哪个窗口中显示该网址。

异步方法和同步方法

大多数Chrome API方法都是异步的:它们会立即返回,而无需等待操作完成。

如果扩展程序需要知道异步操作的结果,它可以将回调函数传递给方法。该方法返回后,可能会在稍后执行回调。

如果扩展程序需要将用户当前选择的选项卡导航到新 URL,则需要获取当前选项卡的 ID,然后将该选项卡的地址更新为新 URL。

如果 tabs.query 方法是同步的,代码可能如下写:

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

上面代码是是失败的,因为 query()是异步的。它会立即返回而不等待工作完成,并且不返回值。当回调参数在其签名中可用时,方法是异步的。

// Signature for an asynchronous method
chrome.tabs.query(object queryInfo, function callback)

要正确查询选项卡并更新其 URL,扩展程序必须使用 callback 参数。

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

在上面的代码中,执行顺序是:1,4,2。调用 query() 指定的回调函数,然后执行第 2 行,但是仅在有关当前所选选项卡的信息可用之后。

这发生在 query()返回后的某个时间。尽管 update()是异步的,但代码不使用回调参数,因为扩展程序不会对更新结果执行任何操作。

// 同步方法

string chrome.runtime.getURL()

此方法同步将URL作为字符串返回,并且不执行其他异步工作。

五、页面之间的通信

扩展程序中的不同组件通常需要彼此通信,不同的 HTML 页面可以使用 chrome.extension 方法找到彼此,例如 getViews()getBackgroundPage()

一旦页面引用了其他扩展页面,第一个页面就可以调用其他页面上的函数并操纵它们的 DOM。

此外,扩展的所有组件都可以访问使用存储API存储的值,并通过消息传递进行通信。

六、数据存储和隐身模式

扩展程序可以使用storage API,HTML5 Web 存储 API 或通过发出保存数据的服务器请求来保存数据。当扩展程序需要保存某些内容时,首先要考虑它是否来自隐身窗口。默认情况下,扩展程序不会在隐身窗口中运行。

隐身模式承诺窗口不会留下任何曲目。处理来自隐身窗口的数据时,扩展程序应遵守此承诺。

如果扩展程序通常会保存浏览历史记录,请不要从隐身窗口保存历史记录。但是,扩展程序可以存储来自任何窗口的设置首选项,隐身或不隐身。

要检测窗口是否处于隐身模式,请检查相关选项卡的隐身属性tabs.Tabwindows.Window 对象。

function saveTabData(tab) {
  if (tab.incognito) {
    return;
  } else {
    chrome.storage.local.set({data: tab.url});
  }
}