转载

【转载】从零深入Chrome插件开发

温馨提示:
本文最后更新于 2024年03月03日,已超过 57 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我
作者:谢小飞
 
使用Chrome插件可以为Chrome浏览器带来一些功能性的扩展,进而提高使用体验;俗话说的好Chrome没插件,香味少一半,Chrome最大的优势还是其支持众多强大好用的扩展程序;今天我们就来了解一下插件是如何开发的,和我们普通的浏览器页面开发有什么区别,以及安利一些功能强大的插件。

Chrome插件简介

  Chrome插件,官方名称extensions(扩展程序);为了方便理解,以下都称为Chrome插件,或者简称插件,那么什么是Chrome插件呢?

扩展程序是自定义浏览体验的小型软件程序。它们让用户可以通过多种方式定制Chrome的功能和行为。

  插件程序提供了以下几个功能:

  • 生产力工具:
  • 网页内容丰富
  • 信息聚合
  • 乐趣和游戏

  我们可以通过点击更多工具 -> 扩展程序来查看我们所有安装的插件,或者直接打开插件标签页

  那么学习开发插件有什么意义呢?我们为什么要来学习插件开发呢?个人总结下,有以下几方面的意义:

  • 增强浏览器功能,实现属于自己的定制插件功能
  • 了解现有的插件,对其功能进行优化改进

获取插件

  大多数Chrome用户从Chrome网上应用店获得插件程序。世界各地的开发人员会在Chrome网上应用店中发布他们的插件,经过Chrome的审查并向最终用户提供。

  但是由于一些众所周知的原因,我们并不能访问网上应用店,但同时Chrome又要求插件必须从它的Chrome应用商店下载安装,这仿佛是一个绕不开的死循环,不过俗话说魔高一尺道高一尺一,下面我们会讲解如何从本地加载插件,绕开网上应用店的限制。

插件如何工作?

  插件是基于Web技术构建的,例如HTML、JavaScript和CSS。它们在单独的沙盒执行环境中运行并与Chrome浏览器进行交互。

  插件允许我们通过使用API修改浏览器行为和访问Web内容来扩展和增强浏览器的功能。插件通过最终用户UI和开发人员API进行操作:

  • 扩展用户界面,这为用户提供了一种一致的方式来管理他们的扩展。
  • 扩展的API允许浏览器本身的扩展的代码访问功能:激活标签,修改网络请求等等。

  要创建插件程序,我们需要组合构成插件程序的一些资源清单,例如JS文件和HTML文件、图像等。对于开发和测试,可以使用扩展开发者模式将这些“解压”加载到Chrome中。如果我们对自己开发出来的插件程序感到满意,就可以通过网上商店将其打包并分享给其他的用户。

插件的原则

  我们编写的插件想要发布到Chrome网上应用店中,就必须遵守网上应用店政策,它规定了以下几点:

  • 插件必须满足一个定义狭窄且易于理解的单一目的。单个插件可以包含多个组件和一系列功能,只要一切都有助于实现一个共同的目的。
  • 用户界面应该是最小的并且有意图。它们的范围可以从简单的图标到打开一个带有表单的新窗口。

一个简单的小插件

  Chrome插件并没有很严格的项目结构要求,比如src、public、components等等,因此我们如果去看很多插件的源码,会发现每个插件的项目结构,甚至项目下的文件名称都大相径庭;但是在根目录下我们都会找到一个manifest.json文件,这是插件的配置文件,说明了插件的各种信息;它的作用等同于小程序的app.json和前端项目的package.json。

  我们在项目中创建一个最简单的manifest.json配置文件:

{
    // 插件名称
    "name": "Hello Extensions",
    // 插件的描述
    "description" : "Base Level Extension",
    // 插件的版本
    "version": "1.0",
    // 配置插件程序的版本号,主流版本是2,最新是3
    "manifest_version": 2
}

  我们经常会点击右上角插件图标时弹出一个小窗口的页面,焦点离开时就关闭了,一般做一些临时性的交互操作;在配置文件中新增browser_action字段,配置popup弹框:

{
    "name": "Hello Extensions",
    "description" : "Base Level Extension",
    "version": "1.0",
    "manifest_version": 2,
    // 新增popup弹框
    "browser_action": {
      "default_popup": "popup.html",
      "default_icon": "popup.png"
    }
}

  然后创建我们的弹框页面popup.html:

<html>
  <body>
    <h1>Hello Extensions</h1>
  </body>
</html>

  点击图标后,插件显示popup.html。

  为了用户方便点击,我们还可以在manifest.json中设置一个键盘快捷键的命令,通过快捷键来弹出popup页面:

{
  "name": "Hello Extensions",
  "description" : "Base Level Extension",
  "version": "1.0",
  "manifest_version": 2,
  "browser_action": {
    "default_popup": "popup.html",
    "default_icon": "popup.png"
  },
  // 新增命令
  "commands": {
    "_execute_browser_action": {
      "suggested_key": {
        "default": "Ctrl+Shift+F",
        "mac": "MacCtrl+Shift+F"
      },
      "description": "Opens popup.html"
    }
  }
}

  这样我们的插件就可以通过按键盘上的Ctrl+Shift+F来弹出。

加载及调试插件

  我们开发的插件需要在浏览器里面运行,打开插件标签页,打开开发者模式,点击加载已解压的扩展程序,选择项目文件夹,就可将开发中的插件加载进来。

开发中更改了代码,点击插件右下角刷新按钮即可重新加载

  如果我们的代码中有错误,加载插件后,会显示红色的错误按钮

  点击错误按钮以查看错误的日志:

  我们上面说过Chrome插件只能从网上应用店中下载安装,但是第三方平台也提供了下载的渠道,下载下来的文件后缀是.crx的压缩包,现在的问题就是如何将crx文件进行安装了。

  从Chrome 73版本开始,谷歌修改了插件策略,不可以随意安装crx文件:如果直接将crx文件拖拽安装可能会提示一下报错:

程序包无效:"CRX_HEADER_INVALID"

  我们可以尝试以下几种方法,第一种方法:将crx后缀改为zip,解压后加载已解压的扩展程序的方式,将插件用开发者模式进行加载。

  第二种办法,通过Chrome插件伴侣,将crx提取到桌面,然后还是用开发者模式进行加载。

  使用插件伴侣提取插件后,插件内容默认会被放在你的电脑桌面上,可以把它剪切/复制到任意位置;加载插件选择的文件夹路径时,一定要包含manifest.json文件;加载后请勿删除提取的文件夹。

  第三种方法就是用梯子了,直接去网上应用店下载,没有梯子的同学可以使用插件伴侣进行代理,插件伴侣的获取方式下文会给出。

 

 

后台background

  我们的插件安装后,popup页面也运行了;但是我们也发现了,popup页面只能做临时性的交互操作,用完就关了,不能存储信息或者和其他标签页进行交互等等;这时就需要用到background(后台),它是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的;它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。

  background也是需要在manifest.json中进行配置,可以通过page指定一张网页,或者通过scripts直接指定一个js数组,Chrome会自动为js生成默认网页:

{
  "background": {
    // "page": "background.html",
    "scripts": ["background.js"],
    "persistent": true
  }
}

  需要注意的是,page属性和scripts属性只需要配置一个即可,如果两个同时配置,则会报以下错误信息:

Only one of 'background.page', 'background.scripts', and 'background.service_worker' can be specified.

  我们给background设置一个监听事件,当插件安装时打印日志:

// background.js
chrome.runtime.onInstalled.addListener(function () {
  console.log("插件已被安装");
});

  点击查看视图旁边的背景页,看到我们设置的background:

storage存储

  我们在插件安装时在storage中设置一个值,这将允许多个插件组件访问该值并进行更新操作:

//background.js
chrome.runtime.onInstalled.addListener(function () {
  // storage中设置值
  chrome.storage.sync.set({ color: "#3aa757" }, function () {
    console.log("storage init color value");
  });
  // 为特定的网址显示图标
  chrome.declarativeContent.onPageChanged.removeRules(undefined, function () {
    chrome.declarativeContent.onPageChanged.addRules([
      {
        conditions: [
          new chrome.declarativeContent.PageStateMatcher({
            pageUrl: { hostEquals: "baidu.com" },
          }),
        ],
        actions: [new chrome.declarativeContent.ShowPageAction()],
      },
    ]);
  });
});

  chrome.declarativeContent用于精确地控制什么时候显示我们的页面按钮,或者需要在用户单击它之前更改它的外观以匹配当前标签页。

  这里调用的chrome.storage和我们常用的localStorage和sessionStorage不是一个东西;由于调用到了storage和declarativeContent的API,因此我们需要在manifest中给插件注册使用的权限:

{
  // 新增
  "permissions": ["storage", "declarativeContent"],
  "background": {
    "scripts": ["background.js"],
    "persistent": true
  }
}

  再次查看背景页的视图,我们就能看到打印的日志了;既然可以存储,那也能取出来,我们在popup中添加事件进行获取,首先我们新增一个触发的button:

<!-- popup.html -->
<html>
  <head>
    <style>
      button {
        width: 60px;
        height: 30px;
        outline: none;
      }
    </style>
  </head>
  <body>
    <button id="changeColor">change</button>
    <script src="popup.js"></script>
  </body>
</html>

  我们再创建一个popup.js的文件,用来从storage存储中拿到颜色值,并将此颜色作为按钮的背景色:

let changeColor = document.getElementById("changeColor");

changeColor.onclick = function (el) {
  chrome.storage.sync.get("color", function (data) {
    changeColor.style.backgroundColor = data.color;
  });
};

如果需要调试popup页面,可以在弹框中右击 => 检查,在DevTools中进行调试查看。

  我们多次打开popup页面,发现页面每次点开按钮都会恢复最开始的默认状态。

获取浏览器tabs

  现在,我们获取到了storage中的值,需要逻辑来进一步与用户交互;更新popup.js中的交互代码:

// popupjs
changeColor.onclick = function (el) {
  chrome.storage.sync.get("color", function (data) {
    let { color } = data;
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
      chrome.tabs.executeScript(tabs[0].id, {
        code: 'document.body.style.backgroundColor = "' + color + '";',
      });
    });
  });
};

  chrome.tabs的API主要是和浏览器的标签页进行交互,通过query找到当前的激活中的tab,然后使用executeScript向标签页注入脚本内容。

  manifest同样需要activeTab的权限,来允许我们的插件使用tabs的API。

{
  "name": "Hello Extensions",
  // ...
  "permissions": ["storage", "declarativeContent", "activeTab"],
}

 

正文到此结束