导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

冷门技能

介绍

简单来说,Chrome 插件是用来增强浏览器或页面功能的程序。我们可以用它来实现「文章预计阅读时间」、「页面一键暗黑模式」、「Chrome Tab 分组管理」等功能。开发 Chrome 插件主要用到的技术是 HTML、CSS 和 JavaScript ,对 Web 开发人员来说非常友好。

文档链接:

Welcome to Chrome Extensions - Chrome Developers

核心项目结构

.
├── background.js        # 后台脚本, e.g. 插件快捷键、popup、关闭选项卡等事件监听
├── images               # 图片资源
│   ├── icon-128.png
│   ├── icon-16.png
│   ├── icon-32.png
│   ├── icon-48.png
├── manifest.json        # 插件的配置清单JSON
├── popup                # 插件弹窗
│   ├── index.css
│   ├── index.html
│   └── index.js
└── scripts              
    └── content.js       # 插入到网页中的脚本

manifest.json

这个文件必须存在于 Chrome 插件中,而且得放在项目根目录,用途是配置所有和插件相关的配置。其中,manifest_versionnameversion 是必不可少的配置字段,descriptionicons 是推荐配置的字段。

{
  "manifest_version": 3,                                    // manifest 版本,必须得是v3
  "name": "Notion Tools",                                   // 插件名称
  "version": "1.0",                                         // 插件版本
  "description": "Enhance the features of notion",          // 插件描述
  "icons": {                                                // 插件 icon 图标
    "16": "images/icon-16.png",
    "32": "images/icon-32.png",
    "48": "images/icon-48.png",
    "128": "images/icon-128.png"
  },
  "content_scripts": [{                                     // 注入页面的内容脚本
    "js": [                                                 // js 路径配置,按数组顺序加载
      "scripts/utils.js",
      "scripts/Outline.js",
      "scripts/content.js"
    ],
    "matches": [                                            // 允许浏览器识别哪些站点将内容脚本注入
      "https:\\/\\/*\\/*"
    ],
    "css": ["outline.css"],                                 // 样式路径
    "run_at": "document_end"                                // 内容脚本执行阶段, 默认为文档空闲时(document_idle)
  }],
  "permissions": ["scripting", "storage"],                  // 插件权限申请
  "web_accessible_resources": [                             // 插件访问资源
    {
      "matches": ["https:\\/\\/*\\/*"],
      "resources": [
        "images/eye.svg",
        "images/eye-hidden.svg"
      ]
    }
  ],
  "action": {                                               // 控制插件的操作 e.g. 默认弹窗配置等
    "default_popup": "popup/index.html"
  },
  "background": {                                           // 后台脚本, e.g. 插件快捷键、popup、关闭选项卡等事件监听
    "service_worker": "background.js"
  },
  "commands": {                                             // 快捷键配置
    "show-outline": {
      "suggested_key": {
        "default": "Ctrl+Shift+O",
        "windows": "Ctrl+Shift+O",
        "mac": "Command+Shift+O",
        "chromeos": "Ctrl+Shift+O",
        "linux": "Ctrl+Shift+O"
      },
      "description": "Show or hide the outline."
    },
		"_execute_action": {                                    // "_execute_action" 键运行的代码与 action.onClicked() 事件相同,因此不需要额外的代码
      "suggested_key": {
        "default": "Ctrl+U",
        "mac": "Command+U"
      }
    }
  },
	"chrome_url_overrides": {
		"newtab": "index.html"                                  // 用 index.html 作为 chrome newTab 页面
	}
}

文档链接:

Welcome to the Chrome Extension Manifest V3 - Chrome Developers

Content Scripts

虽然叫做 Content Scripts ,但它并不限于 JS ,也可以包含 CSS 。我们可以通过在 manifest.json 中配置的方式,把 JS、CSS 注入到页面中去。

{
	"content_scripts": [{
    "js": [                     // js 路径配置,按数组顺序加载
      "scripts/utils.js",
      "scripts/Outline.js",
      "scripts/content.js"
    ],
    "matches": [
      "https:\\/\\/*\\/*"          // "<all_urls>" 表示匹配所有地址
    ],
    "css": ["outline.css"],     // css 路径配置
    "run_at": "document_end"    // 脚本注入的时间,可选值: "document_start" | "document_end" | "document_idle",默认 document_idle 表示页面空闲
  }],
}

⚠️ 注意

如果没有配置 run_atdocument_start,下面这种代码是不会生效的:

document.addEventListener('DOMContentLoaded', function() {
  // todo
});

文档链接:

Chrome Extensions content scripts - Chrome Developers

Background

插件在后台运行的 js 代码,一般把「需要一直运行的」、「启动就运行的」、「全局的」代码放在 background 里面。

{
	"background": {  // 配置插件在后台运行的js代码
    "service_worker": "background.js",
		"type": "module" // 使其支持 ES Module
  },
}

Popup

popup 是点击插件图标时打开的一个弹窗页面,一般用来做一些临时性的交互或插件本身的配置。

{
	"action": {
    "default_popup": "popup/index.html"
  },
}

Background Script 和 Content Scripts 区别

通信

摘自:https://juejin.cn/post/6844903985711677453#heading-4

popupbackground 之间的通信

首先,给一个大致通信图。关于 content scriptpopup scriptbackground script,它们之间的通信总体概览图如下:

Untitled

开始吧。还是和以前一样,新建插件文件夹,增加必须的 manifest.json 和基本文件。

backgroundpopup 发送消息

插件的 background ,对于浏览器只存在一个,而对于 popup ,不同的 tab 就会存在一个前端,如果 background 需要给不同前端发送信息,就需要特殊的 tab id。这里是针对 backgroundpopup 传递信息。

background.js 添加代码:

function toPopup() {
    alert('to popup!')
}

popup.js 添加代码:

const bg = chrome.extension.getBackgroundPage()
document.getElementById('rBgInfo').onclick = function() {
    bg.toPopup()
}

popup.html 引入 popup.js,并添加id为 rBgInfo 的按钮,安装插件,点击按钮,如果弹窗如下样式,则表明成功。

Untitled

popupbackground 发送消息

background => popup 是通过 getBackgroundPage ,而 popup => background 是通过 getViews

下面就来瞧一下

⚪️ 使用长连接

popup.js 增加如下代码:


// 使用长连接
let port = chrome.extension.connect({
    name: 'popup-name'
})

// 使用postMs 发送信息
port.postMessage('给 background 传递信息~')

// 接收信息
port.onMessage.addListener(msg => {
    console.log('接收的信息:', msg)
})

background.js 增加如下代码:

// 获取所有 tab
const pups = chrome.extension.getViews({
    type: 'popup'
}) || []

// 输出第一个使用插件页面的url
if (pups.length) {
    console.log(pups[0].location.href)
}

点击插件刷新按钮,点击【背景页】按钮,可以看到每次点击一下插件图标,就会发送一次信息。

Untitled

这也告诉了 chrome 插件的另一个机制:点击图标出现和隐藏 popup 弹窗页面,实际上是对整个页面的销毁,类似于关闭网页,而不是切换网页。(很重要的哦)

⚪️ 操作 DOM

除了信息传递,background 可能也需要对 popup.html 的页面进行操作,比如检测到当前是万圣节🎃,给插件页面添加个 happy halloween

首先给 popup.html 增加一个 text

<p id="pbText">不是万圣节</p>

然后只需要在 background.js 中如下处理:

// 使用长连接 - 监听 popup 传递来的消息
chrome.extension.onConnect.addListener(port => {
    console.log('连接中------------')
    port.onMessage.addListener(msg => {
        console.log('接收消息:', msg)
        getAll()
        port.postMessage('popup,我收到了你的信息~')
    })
})

// 获取所有 tab
function getAll() {
    const views = chrome.extension.getViews({
        type: 'popup'
    })

    for (let o of views) {
        console.log(111)
        o.document.getElementById('pbText').innerHTML = "万圣节🎃快乐"
    }
}

添加 getAll() 函数,将函数防止长连接即可。这里主要想展示 chrome.extension.getViews 函数的使用。

刷新插件,点击插件图标,就会弹窗如下页面了:

Untitled

popupcontent 之间的通信

有了 backgroundpopup ,下面需要做的就是创建一个 content 页面。

manifest 添加下列配置

{
  ...
  "content_scripts": [
    {
      "matches": [
        "<all_urls>"
      ],
      "js": [
        "content.js"
      ]
    }
  ]
}

contentpopup 发送消息

首先在 content.js 添加如下代码:

// Chrome提供的大部分API是不支持在content_scripts中运行
// sendMessage onMessage 是可以使用
chrome.runtime.sendMessage({
    info: "我是 content.js"
}, res => {
    // 答复
    alert(res)
})
复制代码

代码负责发送信息和接收反馈,然后给 popup.js 添加:

chrome.runtime.onMessage.addListener((req,sender, sendResponse) => {
    sendResponse('我收到了你的来信')
    console.log('接收了来自 content.js的消息', req.info)
})
复制代码

代码负责接收消息和发送反馈。

刷新插件,点击插件按钮,打开一个页面,保持插件 popup 处于活跃状态(上面讲了哈~,插件关闭等于页面销毁),然后刷新页面,会发现浏览器弹出弹窗:

Untitled

最后,右键插件图标,点击“审查弹窗内容”,可以看到 content.jspopup.jsconsole.log 日志(👻这等于告诉您如何调试插件~)

Untitled

弹窗说明我们的程序是成功运行的,日志打印表明我们的通信是成功的,现在我们已经知道了 contentpopup 发送消息。

popupcontent 发送消息

其实上面已经告诉了 popupcontent 发送信息了,但毕竟不是 popup 主动地,谈恋爱了,肯定需要主动一些了。

popup 添加如下代码,放入rBgInfo按钮点击事件:


// popup ---> content
chrome.tabs.query({
    active: true,
    currentWindow: true
}, (tabs) => {
    let message = {
        info: '来自popup的情书💌'
    }
    chrome.tabs.sendMessage(tabs[0].id, message, res => {
        console.log('popup=>content')
        console.log(res)
    })
})

寄送一封信,content 得接收信:

// get popup2content info
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    console.log(request.info)
    sendResponse('我收到了你的情书,popup~')
})

点击插件刷新按钮,打开页面,点击弹窗的rBgInfo按钮,日志打印如下:

Untitled

关于 popupcontent 的通信又又又成功了~

backgroundcontent 之间的通信

backgroundcontent 之间的通信与 popupcontent 类似的,写者就不写demo了,与上面一样。

长连接与短连接

在上面的一些demo中,可以看到通信使用了两个函数,一个就是 sendMessage,另一个就是 connect,其实这两个分别对应着不同的连接方式:

本地开发查看插件开发效果

在开发人员模式下加载未打包的插件:

  1. 地址栏输入 chrome://extensions 打开新 tab 进入插件管理页面
  2. 通过单击页面右侧「开发者模式」旁边的切换开关来启用开发人员模式。
  3. 单击“加载已解压的扩展程序”按钮,然后选择插件目录。

Untitled

参考资源

入门系列3 - background、content、popup的通信

How to Add Keyboard Shortcuts In a Chrome Extension