一、前言 
chrome extension(chorme 浏览器插件)的应用一直都很广泛,它原本的作用就是扩展浏览器的功能而被设计出来的。插件市场也特别繁荣,例如常见的油猴、划词翻译等等插件都非常好用。
接下来话不多说,我们就来看一下,如何一步一步从 0 到 1 开发一款浏览器插件。
二、基础概念 
1.安装 
在浏览器地址栏上输入chrome://extensions/按下回车,就可以看到自己浏览器有多少插件被安装了。
然后再点击加载已解压的扩展程序,选择要安装的插件就可以体验插件的使用了。
2.使用 
一般好的插件都会在加载的那一刻弹出如何使用的页面,但是如果没有弹出,我们就要在action区域去看看如何使用。
点击扩展可以看到所有的扩展,可以通过铆钉将其定位在右上角的插件栏上,再点击图标就可以使用插件了。
3.popup 
点击插件的图标,一般会弹出来一个内容,我们以划词翻译为例。
会弹出来这样一个类似工作台的东西,这个内容是一个独立的 DOM 环境。什么叫做独立的 DOM 呢?就是这个弹出来的内容是由一个单独的 html 组成的,他有自己的 css、js、html,与其他环境隔离。在我们把它叫做 popup 区域,这个区域的代码在popup每次打开的时候都会重新执行。
4.background 
在插件刚安装的时候,也会执行一个脚本叫做 background 脚本,这个脚本也是一个独立的环境,今后没有特殊情况下不会重新执行,是整个插件当中执行频率最低的一个脚本。在 chromeV2 以及之前的版本中 background 有自己的视图页面,但是在 v3 版本中,废弃了这一设计,只有一个脚本来代替,跑在插件的后台。
5.content 
插件之所以起作用,就是因为能够改变当前页面的行为。因此需要有一个脚本(包括 css)来运行在当前页面上,且自插件安装之后,每新开的 tab 页,就会从插件中加载这段脚本运行,我们举个划词翻译的例子:
我在当前站点划一个词,就会弹出一个小图标,我点击小图标就会为我提供翻译的服务。
从这个过程,我们知道,就在当前的 github 页面中,一定有一段监听划词的代码在运行着。而承载这段代码的载体就是 content。并且不管我移到 github 之外的任何站点,这段脚本都会存在且执行。
6.chrome 
以上的三个环境既然都是相互独立的,那么如何进行通信呢?因此在真实的业务场景中,他们其实是需要经常通信的。那就需要一个共同信任的人,这个人就是 chrome 对象。这个对象是浏览器在运行插件的过程中,向三个环境注入的一个对象,它身上有很多方法可以实现互相之间的通信。
如果要了解如何进行通信的细节,可以看我的这一篇文章
7.manifest.json 
这是 chorme 开发的过程中非常重要的一个配置,比如配什么样子的图标、应该向环境中注入哪些变量、各个脚本的路径指向。
我们可以看一下这个文件的样子感受一下:
{
  "name": "Getting Started Example", // 插件名称
  "description": "Build an Extension!", // 插件描述
  "version": "1.0", // 版本
  "manifest_version": 3, // 指定插件版本,这个很重要,指定什么版本就用什么样的api,不能用错了
  "background": {
    "service_worker": "background.js" // 指定background脚本的路径
  },
  "action": {
    "default_popup": "popup.html", // 指定popup的路径
    "default_icon": {
      // 指定popup的图标,不同尺寸
      "16": "/images/get_started16.png",
      "32": "/images/get_started32.png",
      "48": "/images/get_started48.png",
      "128": "/images/get_started128.png"
    }
  },
  "icons": {
    // 指定插件的图标,不同尺寸
    "16": "/images/get_started16.png",
    "32": "/images/get_started32.png",
    "48": "/images/get_started48.png",
    "128": "/images/get_started128.png"
  },
  "permissions": [], // 指定应该在脚本中注入那些变量方法,后文再详细说
  "content_scripts": [
    // 指定content脚本配置
    {
      "js": ["content.js"], // content脚本路径
      "css": ["content.css"], // content的css
      "matches": ["<all_urls>"] // 对匹配到的tab起作用。all_urls就是全部都起作用
    }
  ]
}6.总结 
介绍完了基本的概念,那么我们开发插件的任务本质就是设计 background、popup、content 的代码!已完成业务的需求,了解完基本概念我们就来试试水吧!
三、小试牛刀 
1. hello extensions 
我们定一个需求,写一个浏览器插件,要求点击 action,弹出来一个 popup 上面写上hello world 。
// 新建manifest.json
{
  "name": "Hello Extensions",
  "description": "Base Level Extension",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "hello.html",
    "default_icon": "hello_extensions.png"
  }
}
// 新建popup.js
console.log("This is a popup!");新建 hello.html
<html>
  <body>
    <h1>Hello Extensions</h1>
    <script src="popup.js"></script>
  </body>
</html>找一张图片放在同级目录命名为:hello_extensions.png
按照之前讲到了安装该插件。然后一起来看一看效果!
2. Getting Started Example 
为了方便练习,再一个需求,该插件可以使得切到任何一个页面的背景颜色发生变化。
话不多说,直接准备一个 manifest.json
{
  "name": "Getting Started Example",
  "description": "Build an Extension!",
  "version": "1.0",
  "manifest_version": 3,
  "background": {
    "service_worker": "background.js"
  },
  "permissions": ["storage", "activeTab", "scripting"],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "/images/get_started16.png",
      "32": "/images/get_started32.png",
      "48": "/images/get_started48.png",
      "128": "/images/get_started128.png"
    }
  },
  "icons": {
    "16": "/images/get_started16.png",
    "32": "/images/get_started32.png",
    "48": "/images/get_started48.png",
    "128": "/images/get_started128.png"
  }
}其中其他的比较好理解,但是这个permissions是什么东西呀?这个可以翻译作权限,换句话说,这个数组配置的内容代表分配给环境什么样的权限,因为我们要在插件中存储一个颜色信息所以用storage,因为要搜索当前激活的 tab 所以增加activeTab权限,因为要在 content 中采用注入的方式执行脚本所以用scripting,每一种权限都决定是否在环境中注入一些方法和变量,所以这个至关重要!
新建一个 popup.html
改变颜色的触发时机,我们可以选择放在 popup 中,点击一个按钮,然后当前激活的 tab 背景颜色改变。因为要存一个颜色,因此我们用 storage 维护起来,这个相当于浏览器的 localStorage 一样,持久存储一个状态用的。
// background
const color = "#3aa757";
// 在安装完成之后,执行这样一个代码。相当于插件内部就存储了一个颜色。
chrome.runtime.onInstalled.addListener(() => {
  chrome.storage.sync.set({ color });
});创建一个 popup.html
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="button.css" />
  </head>
  <body>
    <button id="changeColor"></button>
    <script src="popup.js"></script>
  </body>
</html>紧接着创建一个 popup.js
const changeColor = document.getElementById("changeColor");
chrome.storage.sync.get("color", ({ color }) => {
  changeColor.style.backgroundColor = color; // 从内部获取颜色,填充到按钮上,体验更好
});
changeColor.addEventListener("click", async () => {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  // 监听点击事件,如果点击就执行下面的代码,并获取当前激活的tab的id。
  chrome.scripting.executeScript({
    target: { tabId: tab.id },
    func: setPageBackgroundColor,
  });
  // 在当前激活的tab页面中执行setPageBackgroundColor这样一个函数
});
function setPageBackgroundColor() {
  chrome.storage.sync.get("color", ({ color }) => {
    // 值得注意的是该document其实是tab页面的document对象,因为这个函数是在tab页面中执行的。
    document.body.style.backgroundColor = color;
  });
}接下来把图片什么的补充好,就可以开始看看效果了!
四、总结 
以上就是一个简单插件的开发流程,当然这两个例子很简单,但是其实对于入门和了解一个插件开发已完全足够,至少可以让我们了解到,插件开发并没有想象中的那么神秘。本质上就是把 html,css,js 换个方式再玩一遍而已。后期我会更新插件开发中的更多进阶内容,如果有收获希望帮忙点赞,关注一下!万分感激。
五、资源 
上面的案例如果不想自己找图片等琐事,也可直接用官网的案例,效果一致:
