前言
上篇文章 介绍了 chrome
开发者工具中一些常用的功能来帮助提效
除此之外 chrome
还有强大的插件生态,可以满足各种个性化的需求
本篇文章会分成两部分
- 插件推荐
- 插件开发 来帮助你更好地使用插件
插件推荐
TabsPlus
可以设置新标签页出现的位置,我设置成在当前标签页的右边,方便快速打开及关闭新标签页
TOBY
比起
chrome
自带的收藏夹,toby
更为易用且直观
打开新标签页时,会自动打开toby
,显示之前整理的所有页面,平铺展开更为直观便捷
沙拉划词
可以直接在网页中选中对应的英文,即可出现翻译
fehelper
前端常用工具集合
Octotree
将github
层层嵌套的文件,做成一个目录悬浮在左侧,方便我们观察项目结构
Simple Allow Copy
允许在任意网站上复制信息,包括那些需要登录的网站
Sticky Notes
随手记,方便将一些常用的信息记录在上面
rename-tab-title
自定义网页名称
恢复关闭的标签页
快速恢复关闭的页面
darkreader
网页阅读暗黑模式
插件开发
虽然目前 chrome extension 的生态非常完善,但总是会有一些很个性化的需求无法得到满足
因此我们可以选择自己来开发插件
谷歌2021年发布了插件v3版本,并会随着时间推移不再支持v2版本
因此本文基于 v3 版本的规范进行介绍
目录结构
最基础的插件目录,由下面三部分组成
- hello.html 插件界面
- hello_extensions.png 插件图标
- manifest.json 配置文件
其中 manifest.json 是核心,来看一个最简单的配置
{
"name": "Hello Extensions", // 插件名
"description": "Base Level Extension", // 插件描述
"version": "1.0", // 插件版本
"manifest_version": 3, // 谷歌插件版本
"action": {
"default_popup": "hello.html", // 点击插件后弹出的内容(类似iframe)
"default_icon": "hello_extensions.png" // 插件的图标
},
"commands": {
// 打开插件的快捷键
"_execute_action": {
"suggested_key": {
"default": "Ctrl+Shift+F",
"mac": "MacCtrl+Shift+F"
},
"description": "Opens hello.html"
}
}
}
复制代码
hello.html 描述点开后浮层要展示的内容
<html>
<body>
<h1>Hello Extensions</h1>
</body>
</html>
复制代码
加载自己开发的插件
打开开发者模式
通过加载已加载的拓展程序,可以载入我们本地开发的插件
UI层设计与交互
浮层中的html
文件,是支持css
和js
注入的
例如我们改造一下上面的html
文件
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="button.css">
</head>
<body>
<button id="changeColor"></button>
<script src="popup.js"></script>
</body>
</html>
复制代码
然后在根目录新增 button.css
文件和 popup.js
文件
button {
height: 30px;
width: 30px;
outline: none;
margin: 10px;
border: none;
border-radius: 2px;
}
button.current {
box-shadow: 0 0 0 2px white,
0 0 0 4px black;
}
复制代码
// popup.js
let changeColor = document.getElementById("changeColor");
changeColor.addEventListener("click", () => {
changeColor.style.backgroundColor = 'green';
});
复制代码
点击后变成绿色方块
与页面产生交互
既然是chrome
插件,我们肯定希望与网页能产生关系,这里我们可以用到chrome
提供的api
来对网页进行一些修改
例如当我们点击按钮时,改变当前页面的背景色
popup.js
代码改成如下
changeColor.addEventListener("click", async () => {
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); // 获取当前页面
chrome.scripting.executeScript({
// 对目标网页执行指定函数
target: { tabId: tab.id },
function: setPageBackgroundColor,
});
});
function setPageBackgroundColor() {
document.body.style.backgroundColor = 'green';
}
复制代码
注意,由于安全权限控制,这里使用到的两个chrome
的权限(获取tab
,执行js
)都是需要在 manifest
里面显式注册的
"permissions": ["activeTab", "scripting"]
复制代码
提供用户选项
如上目前的插件其实已经有了改变网页颜色的能力,但这个目标颜色是我们写死的,如果我们希望把这个颜色选项开放给用户
那么可以用到插件中的options
选项
修改 manifest.json
,增加一个 options_page
参数
"options_page": "options.html"
复制代码
然后创建 options.html
文件
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="button.css">
</head>
<body>
<div id="buttonDiv">
</div>
<div>
<p>choose background color</p>
</div>
<script src="options.js"></script>
</body>
</html>
复制代码
以及一个 options.js
文件
let page = document.getElementById("buttonDiv");
let selectedClassName = "current";
const presetButtonColors = ["#3aa757", "#e8453c", "#f9bb2d", "#4688f1"];
// Reacts to a button click by marking the selected button and saving
// the selection
function handleButtonClick(event) {
// Remove styling from the previously selected color
let current = event.target.parentElement.querySelector(
`.${selectedClassName}`
);
if (current && current !== event.target) {
current.classList.remove(selectedClassName);
}
// Mark the button as selected
let color = event.target.dataset.color;
event.target.classList.add(selectedClassName);
chrome.storage.sync.set({ color });
}
// Add a button to the page for each supplied color
function constructOptions(buttonColors) {
chrome.storage.sync.get("color", (data) => {
let currentColor = data.color;
// For each color we were provided…
for (let buttonColor of buttonColors) {
// …create a button with that color…
let button = document.createElement("button");
button.dataset.color = buttonColor;
button.style.backgroundColor = buttonColor;
// …mark the currently selected color…
if (buttonColor === currentColor) {
button.classList.add(selectedClassName);
}
// …and register a listener for when that button is clicked
button.addEventListener("click", handleButtonClick);
page.appendChild(button);
}
});
}
// Initialize the page by constructing the color options
constructOptions(presetButtonColors);
复制代码
我们需要思考一个问题,options
选中的颜色如何传递到 popup.js
中呢
这里我们就用到了chrome
的另一个api
,storage
,它类似一个全局对象,可以帮助做数据存储和传递
因此对应地我们要修改下 popup.js
中变色的函数
function setPageBackgroundColor() {
chrome.storage.sync.get("color", ({ color }) => {
document.body.style.backgroundColor = color;
});
}
复制代码
效果如下
options
页面:
变色效果:
初始化函数
当前代码还有一个问题,就是当用户不打开选项页面进行选择时,缺少一个初始化颜色的过程(option.js
不会被执行)
因此我们需要引入一个新的部件,background script
就是在后台运行的脚本,它的作用是赋予扩展监控浏览器行为的能力,例如插件被安装,打开了一个新的页面,移除了书签,关闭了一个标签页等,通过注册事件可以对感兴趣的浏览器事件做出反馈
在这个场景中,我们可以增加这么一个 background.js
let color = '#3aa757';
// 插件被完成安装
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.sync.set({ color });
console.log('Default background color set to %cgreen', `color: ${color}`);
});
复制代码
同时在 manifest 中增加如下配置
"background": {
"service_worker": "background.js"
}
复制代码
则该插件在被完成安装后,就会在storage
中记录默认的颜色
background
的生命周期是插件中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background
里面
注意background中不能操作DOM
注入页面
针对这个改变页面颜色的例子,如果我们觉得每次打开一个页面后,需要点击一下按钮才变色比较麻烦
那这个时候就可以用到另一种脚本 content_scripts
通过url匹配规则,可以直接把 css
样式或 js
注入到当前浏览的页面中
在 manifest
中增加配置
"content_scripts": [
{
// 满足matches匹配的域名
"matches": ["https://*.com/*"],
// 注入css
"css": ["my-styles.css"],
// 注入js
"js": ["content-script.js"],
// 注入的时机
"run_at": "document_idle" | "document_start" | "document_end"
}
]
复制代码
例如这个例子,我们就可以注入这么一个 js
文件
chrome.storage.sync.get("color", ({ color }) => {
document.body.style.backgroundColor = color;
});
复制代码
效果就是只要一打开网页,背景色就变成了绿色
右键菜单
如果我们想要另一种交互效果,即在页面右键中点击变色后,页面才变色
此时可以在 Background.js
中增加以下代码
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.sync.set({ color });
chrome.contextMenus.create({
title: "change color",
contexts: ["all"],
id: "color"
});
});
复制代码
表示在右键菜单中增添一项
效果:
然后我们需要增加这个菜单项的点击事件,由于在 background
中不能操作 dom
因此我们选择发送一个消息给到 content_script
chrome.contextMenus.onClicked.addListener(function (info, tab) {
if (info.menuItemId === "color") {
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, {from: "background.js"}, response => {
console.log("background -> content script infos have been sended");
}
)});
}
})
复制代码
然后在 content_script
中监听接收这个消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
// 发送回传
sendResponse({number: request.number});
chrome.storage.sync.get("color", ({ color }) => {
document.body.style.backgroundColor = color;
})
}
);
复制代码
这里有个坑,就是 sendResponse
这个回传是必须的,可以类比一次握手,需要消息互通
覆盖浏览器行为
正如我们所看到的一些浏览器插件,比如 Toby
安装以后,每次打开新的标签页,都会变成 toby
的页面
这里是用到了浏览器行为覆盖
"chrome_url_overrides": {
"newtab": "index.html"
}
复制代码
图标配置
icons
包含url
地址栏后面显示的图标,放在应用市场展示的图标等等,通过default_icon
进行配置:
"action": {
"default_icon": {
"16": "extension_toolbar_icon16.png",
"32": "extension_toolbar_icon32.png",
"48": "extension_toolbar_icon48.png",
"128": "extension_toolbar_icon128.png"
}
}
复制代码
关于这四种尺寸有如下说明:
Size | Icon Use |
---|---|
16x16 | favicon on the extension's pages |
32x32 | Windows computers often require this size. Providing this option will prevent size distortion from shrinking the 48x48 option. |
48x48 | displays on the extensions management page |
128x128 | displays on installation and in the Chrome Webstore |
个人小demo
基于我们目前介绍的内容,已经可以定制实现很多功能了
比如我做的这个非常简单的小 demo
在浏览网页时,有时会遇到一些有用的信息想快速记录下来,可以通过该插件添加右键菜单,一键写入暂存笔记中
选中文本添加后,在我们的插件中可 查看/编辑 记录的内容,内容存储是跨网页的,生命周期伴随整个浏览器