Chrome 扩展学习

前言

本文是一篇关于 Chrome 扩展(插件)开发基本知识的文章(V2,V3 请移步)。

对于现在开发 Chrome 扩展来说,是非常简单的一件事情,其只需要使用 JavaScript 即可开发,并且 Chrome 官方对你如何开发 Chrome 扩展程序并没有严格要求,

只需要在你项目的根目录保证存在 mainfest.json 文件即可,该文件是用来配置所有和插件相关的配置,必须放在根目录,它就相当于 webpack 的入口文件。

manifest.json 配置详情

快速开始

  1. 任意位置创建一个项目
  2. 在项目根目录创建一个名为 manif~~~~est.json 的文件
  3. 使用任意 IDE 通过 JavaScript 进行项目开发
  4. 将项目打包成 .crx 文件安装到 Google 中

    1. Google 中搜索:chrome://extensions/ 打开扩展程序
    2. 右上角开启开发者模式
    3. 点击打包扩展程序并将对应的项目文件夹打包。

    或直接使用文件夹的形式安装到 Google 中

    1. Google 中搜索:chrome://extensions/ 打开扩展程序
    2. 右上角开启开发者模式
    3. 点击加载已解压的扩展程序并选择对应的项目文件夹

现在你可以在 Google 中使用你的扩展程序了

manifest.json 字段介绍

❗️ 称呼规定

  • contentScripts 在本节中指:使用 content_scripts 字段配置的脚本文件。
  • injectJS / inject-*:在 contentScripts 中向指定页面注入的脚本文件。
  • popup:使用 browser_actionpage_action 字段的选项 default_popup 配置的页面

EN-background | CN

What's the background?

background 通常翻译为:后台。

它是一个常驻的页面,其生命周期是扩展中所有类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭。

所以它的作用的通常是:把需要一直运行的、启动就运行的以及全局代码放里面去执行。

并且 background 的权限非常高,几乎可以调用所有的 Chrome 扩展 API(除了 devtools),而且它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置 CORS

经过测试,其实不止是 background,所有的直接通过 chrome-extension://id/xx.html 这种方式打开的网页都可以无限制跨域。

所以通常我们可以使用 chrome.extension API 的 getUrl() 将项目中的相对路径转为完成的 URL,它会转为这样的格式:chrome-extension://xx.xx
而正也正好使得相对路径所对应的 .js 或 .html 文件可以无限制跨域。

格式

 { 
  "background":{
  // page 和 scripts 二选一
  "page"?:"background.html",
  
  // 项目中的任意脚本
  "scripts"?:[
  "background.js",
  "xx.js"?,
  "..."?
  ],
  
  // 可选;设置该 background(后台)是否是持久化的,通常是 false(默认值)
  "persistent"?: false
  }
 }
  • page:指定一个 .html 文件 scripts:指定一个 .js 文件

    若你使用 scripts,则 chrome 会自动为该脚本生成一个默认网页

  • 使用 page 的好处在于:你可以配置 background(后台)的页面
  • 不论是使用 page 或者是 scripts,它们都能搭配使用 persistent

    也可以忽略不使用 persistent,它的默认值为:false

background 在 Chrome 的何处?

  1. 在 Chrome 搜索:chrome://extensions/
  2. 在跳转的页面右上角打开开发者模式
  3. 在任意已启用的扩展程序中你可以看见蓝色字体显示的:【背景页】或【background page】字段,
  4. 点击【背景页】或【background page】字段,就会弹出一个供开发者调试的 background 页面。

而这就是我们在 manifest.jsonbackground 字段配置的 .html 或 .js。

note

  • 通过点击【背景页】显示的 background 页面(后台页面)和真正在运行的 background 并非同一个,

    弹出的后台页面只是供你调试用的,参见:此处

  • 只有处于开发者模式下且启用了的扩展程序才能查看 【background page】
  • 通常来说 background 字段选项 persistent 的值通常为 false,参见:此处
  • 若你不主动查看 background.js,则即使它报错导致程序崩溃,它也并不会主动提示任何信息。

page_action | browser_action | app(discard)

介绍

manifest.json 中存在以下三个字段:

  1. page_action
  2. browser_action
  3. app

但是这三个字段只能选择,也必须选择 1 个使用,不可选择多个,否则 Chrome 将会加载失败。

EN-page_action

当某些特定页面打开才显示的图标z

 {
  "page_action": {
  "default_popup": "html/pageaction.html",
  "default_title": "鼠标移动到扩展程序时将提示的信息",
  "default_icon": "img/sds.png",
  // 也可以选择多张图片,可以为不同的比例设置不同的图片,将自动应用!
  "default_icon": {
  "16": "img/sds.png",
  "32": "img/sds.png",
  "48": "img/sds.png",
  "128": "img/sds.png"
  }
  }
 }

Tips

For the best visual impact, follow these guidelines:

  • Do use page actions for features that make sense for only a few pages.
  • Don't use page actions for features that make sense for most pages. Use browser actions instead.
  • Don't constantly animate your icon. That's just annoying.

EN-browser_action

其用处类似于 page_action 字段,但是 browser_action 可以显示在任何页面!

 {
  "browser_action": {
  "default_popup": "html/browseraction.html",
  "default_title": "鼠标移动到扩展程序时将提示的信息",
  "default_icon": "img/icon.png",
  // 也可以选择多张图片,可以为不同的比例设置不同的图片,将自动应用!
  "default_icon": { 
  "16": "images/icon16.png", 
  "24": "images/icon24.png", 
  "32": "images/icon32.png" 
  }
  }
 }

app(已被弃用)

page_action 和 browser_action 有什么用?

page_actionbrowser_action 两个字段用来设置扩展程序在用户的”眼中”是什么样子的。

即:设置扩展程序【向外展示的图标(头像)】、【点击扩展程序弹出的页面】以及【鼠标移动到扩展程序时弹出的提示】是什么。

在扩展程序界面( chrome://extensions/)显示的图标是 icons 字段配置的。

请看下图:
Chrome 扩展学习_第1张图片
(what-is-use-browser-action)

以上图片各个弹出框在 page_actionbrowser_action 字段中的对应选项如下所示:

  • browser_action 所在的弹出框则是 "default_popup" 选项配置的(popup)。
  • 鼠标移动到扩展程序时将提示的信息 弹出框则是由:"default_title" 选项配置。
  • 红色圆圈括起来的“礼物盒“就是 "default_icon" 选项配置的;

popup

概念

popup 是点击 browser_action 或者 page_action 图标时打开的一个小窗口网页,焦点离开网页就立即关闭,一般用来做一些临时性的交互,

对于 browser_actionpage_action 来说,default_popup 选项无疑是非常重要的,它是配置 popup 的关键。

你可以在 page_action 和 browser_action 有什么用? 一节中找到有关于 popup 的描述。

popup 可以包含任意你想要的 HTML 内容,并且会自适应大小。这意味着你完全可以在 popup 中加载一些指定的脚本文件,来执行一些命令。

如何设置 popup?

有两种方法:

  1. default_popup字段来指定 popup(推荐)
  2. 调用 setPopup() 方法。
popup 的权限

在权限上,它和 background 非常类似——几乎可以调用所有的 Chrome 扩展 API(除了 devtools),而且它可以无限制跨域;

它们之间最大的不同是生命周期的不同,popup中可以直接通过 chrome.extension.getBackgroundPage() 获取 background 的 window 对象。

EN-content_scripts | CN

配置选项格式

 {
  // 需要直接注入页面的JS
  "content_scripts": 
  [
  {
  // 指定注入的页面地址(必要)
  "matches": [
  ""?, // 表示匹配所有地址,配置了这个就没必须要配置其他的
  "http://www.whyhw.com/" // 指定注入的页面,注:最后面的 '/' 是必要的
  ],
  
  // 多个 JS 按顺序注入(可选)
  "js": ["js/jquery-1.8.3.js", "js/content-script.js"],
  
  // 多个 CSS 按顺序注入(可选)
  "css": ["css/custom.css","css/xx.css"],
  
  // 代码注入的时间(可选)
  "run_at": "document_start"
  },
  
  // 这里仅仅是为了演示content-script可以配置多个规则
  {
  "matches": ["*://*/*.png", "*://*/*.jpg", "*://*/*.gif", "*://*/*.bmp"],
  "js": ["js/show-image-content-size.js"]
  }
  ],
 }

### What's the content_scripts?

contentScripts 是在网页上下文中运行的文件。通过使用标准的[文档对象模型](http://www.w3.org/TR/DOM-Level-2-HTML/)(DOM),contentScripts 能够读取浏览器访问的网页的详细信息,对其进行更改并将信息传递给其父扩展。

`manifest.json` 的 `content_scripts` 字段的作用是:把**脚本**和 **CSS** 注入到**指定**页面。

contentScripts 的缺陷及解决方法

缺陷

contentScripts 虽然运行在指定网页的上下文中,可以访问指定页面的 DOM,但是却无法访问指定页面的其他脚本文件;指定页面的 DOM 也无法主动调用 contentScripts 中的代码。

=> 比如:

 
   
 
 
 const cs = () => console.log('contentScripts')
  • 当我们执行 a.html 并点击其中的按钮时,

    不会在控制台打印 contentScripts,而是会报错,cs 函数未定义

这是由于 contentScripts 不会被真正注入到页面中(它不会存在于页面),导致无法在 DOM 中主动通过绑定事件的方式调用 content-script 中的代码,

包括直接写 onclickaddEventListener 2种方式都不行;但是,“在页面上(a.html)添加一个按钮并调用指定插件的扩展API”是一个很常见的需求,

比如:你想为一个指定域名的网站设置指定扩展,此时:你可以让网站”配合“你,从而为它定制出一个独特的 Chrome 扩展。

所以这就需要一个解决方法,详见:contentScripts 缺陷的解决方法

contentScripts 缺陷的解决方法

解决方法的理论也很简单,即:在 contentScripts 中,通过某种方式向指定页面注入一个 / 多个脚本文件(_或是其他的一些什么_)

这样你就可以在页面上主动的调用注入到页面的脚本或访问页面中的其他脚本,因为此时,你注入的脚本文件就属于指定页面了,而并非单纯的如同 contentScripts 中那样,是运行在网页的上下文中。

如:

 // contentScripts
 ​
 // 向页面注入 inject.js
 function injectCustomJs(jsPath) {
  jsPath = jsPath || 'js/inject.js';
  var tempScript = document.createElement('script');
  tempScript.setAttribute('type', 'text/javascript');
  tempScript.src = chrome.extension.getURL(jsPath);
  document.head.appendChild(tempScript); // 将指定 js 注入(添加)指定页面中
  // 当注入的脚本加载完毕后移除它
  tempScript.onload = function () {
  this.parentNode.removeChild(this);
  };
 }
 /** 
  * HTML被完全加载以及解析时,触发该事件
  * 这时候,我们才将之注入到页面,否则会因为 HTML 未加载,脚本就注入完成,导致 DOM 中绑定的事件无效。
  */
 document.addEventListener('DOMContentLoaded', () => {injectCustomJs()}) 

 // inject.js
 const cs = () => console.log('inject.js')

在做完往如上配置后,还必须在 manifest.json 中显示配置:

 { 
  // 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的
  "web_accessible_resources": ["js/inject.js"], // 在这里配置你注入的任意脚本!
 }

该字段表示:想要在 web 中直接访问插件中的资源的话必须显示声明,否则会报错。

当一切都搞定后,此时,当我们点击 a.html 中的按钮时,控制台将会打印 inject.js;这就表明我们已经成功解决指定页面的 DOM 无法调用扩展的脚本。

更多信息,参见:inject-scripts

injectJS 不能访问 contentScripts?

虽然在 contentScripts 缺陷的解决方法 一节中,我们解决了指定页面的 DOM 访问扩展的脚本以及 contentScripts 访问页面的其他脚本问题,

但是该解决方法存在一个问题,即:injectJS 无法访问 contentScripts,这是因为二者是不同的,injectJS 就相当于页面上的脚本,而 contentScripts 仍然是运行在网页的上下文中;

contentScripts 访问不了页面的其他脚本文件,也自然无法访问 injectJS,反过来也是一样的:injectJS 无法通过普通方式访问 contentScripts。

那么该怎么解决呢?

参见:消息通信之 injectJS 和 contentScripts

详见:injecteJS 和 contentScripts 通信方法

note

  • contentScripts 将先于指定页面加载

    这意味着你需要首先在 contentScripts 中判断当前要注入 contentScripts 的 HTML 是否加载完毕,你才能获取到 DOM 元素(如:document.head 等)

Reference

Doc

API

你可能感兴趣的