前言
做为一个程序员,大家多多少少应该都用过 Chrome extensions
吧~ 像前端用的比较多的 Proxy SwitchyOmega 用于管理和切换代理设置,还有React Developer Tools 用于调试开发 react 项目等等。那么这些chrome 插件又是怎么开发呢?这篇文章会从开发到发布一步步带大家开发一款chrome 插件~
Chrome Extension 是什么
从官网的介绍来看:
Extensions are small software programs that customize the browsing experience. They let users tailor Chrome functionality and behavior in many ways. Extensions are built on web technologies such as HTML, JavaScript, and CSS. They run in a separate, sandboxed execution environment and interact with the Chrome browser.
扩展是自定义浏览体验的小型软件程序。 它们让用户可以通过多种方式定制 Chrome 的功能和行为。扩展是基于 HTML、JavaScript 和 CSS 等 Web 技术构建的。 它们在单独的沙盒执行环境中运行,并与 Chrome 浏览器交互。
所以Chrome插件其实就是一个由HTML、CSS、JS、图片等资源组成的一个.crx后缀的压缩包。
开发一款插件可以非常灵活,既可以用纯 JS 开发,也可以 webpack
打包编译用React Vue
开发,更可以用轻便的 Svelte
开发。本篇文章主要侧重从开发到发布的流程,所以就用最简单JS 来介绍~
看完这篇文章大家可以获得:
- 如何开发调试一个Chrome 扩展程序
- Chrome Extension 如何发送请求
- 如何发布一个Chrome Extension
感兴趣的朋友就继续看下去吧~
开发与调试
基础概念
manifest.json
插件的配置文件,需要放在根目录,本篇文章介绍几个比较重要且常用的~其余请看官网文档:
{
// 插件名字,required
"name": "extension",
// 插件描述
"description": "Tool",
// 使用版本,目前已经升级到 3
"manifest_version": 3,
// 插件版本
"version": "1.0",
"action": {
// 插件展示的图标
"default_icon": "img/activeIcon.png",
// 插件展示标题
"default_title": "Tool",
// 可选, 点击插件展示的小窗口,也可以监听点击事件,渲染页面
"default_popup": "./html/popup.html"
},
// 注册后台脚本,具体功能后续会提到
"background": {
// required
"service_worker": "js/background/index.js",
},
// 注册页面脚本,具体功能后续会提到
"content_scripts": [
{
"matches": [
"https://*.nytimes.com/*"
],
"run_at": "document_idle",
"js": [
"contentScript.js"
]
}
],
// 插件需要申请的权限,重要,在填写发布时需要填写理由
"permissions": [
"cookies",
"background",
"<http://localhost/*>"
],
"icons": {
"16": "img/activeIcon.png",
"48": "img/activeIcon.png",
"128": "img/activeIcon.png"
}
}
popup
点击 extension icon 后打开的自定义窗口,生命周期为从 popup 弹出到 popup 关闭
background
首先明确 Chrome Extension 的功能,扩展程序是基于事件的程序,用于修改或增强 Chrome 浏览体验,因此 我们可以把Chrome Extension 的工作分成三个部分,浏览器,插件(即 popup), background
当插件想要触发浏览器,例如导航到新页面、删除书签或关闭选项卡等等。background.js 中的脚本监视这些事件,然后根据指定的指令做出反应。
backgroud
,主要用来提供全局配置、事件监听、业务转发等。所以它的生命周期和浏览器一致,无跨域限制。几乎可以调用所有的 API,除了 chrome.devtools.
。
为了更好理解 backgroud,这里插入一个小DEMO,让大家更好的理解
功能: 监听某 cookie 变化,切换当前插件图标
/**
* @file backgroud.js
*
*/
/**
* 监听cookie变化,cookie发生变化时,更新icon状态
*
*/
chrome.cookies.onChanged.addListener(
throttle(function (changeInfo) {
try {
const {
cookie: { domain },
// 获取变化的cookie所在域名
} = changeInfo || {}
if (domain) {
// 判断是不是指定域名下的cookie
const isValid = testCurrentTabUrl(`https://${domain}`)
if (isValid) {
updatePageActionStatus(`https://${domain}`)
}
}
} catch (error) {}
}),
)
/**
* 更新chrome extension 图标
* @param tabId number
* @param origin string Url
*/
function updatePageActionStatus(origin) {
if (!!origin) {
// 获取改域名下的所有cookie
chrome.cookies.getAll(
{
url: origin,
},
function (cookieLists) {
// 判断该cookie中是否active,是,图标置为激活状态
let isActive
// 设置icon
chrome.action.setIcon({
path: !!isActive
? '../../img/activeIcon.png'
: '../../img/inActiveIcon.png',
})
},
)
} else {
chrome.action.setIcon({
path: '../../img/inActiveIcon.png',
})
}
}
Content Scripts
有些扩展程序可以直接改变当前浏览页面的样式,就是通过 content script
向页面注入 js 或 css,从而控制页面的 dom。并且在 content scripts 中只能使用 chrome.extension.*
、chrome.i18n.*
、chrome.storage.*
以及chrome.runtime.*
相关的 API,如果需要使用其他的 extension API ,需要借助消息传递让 background
调用。
消息传递(Message Passing)
一次性简单请求
发请求
chrome.runtime.sendMessage
chrome.tabs.sendMessage
监听该请求发送
chrome.runtime.onMessage
这里插入一个DEMO
功能:用户在 popup 中点击,由 background
发送请求,因为在background
中无跨域限制
/**
* @file popup.js
*
*/
chrome.runtime.sendMessage(
{
type: LIB_ACTION.FETCH_LIST,
data: {},
},
function (response) {
const { data = {}, origin, error } = response || {}
if (error) {
handleErrorSendMessage()
} else {
// 更新 popup 样式
renderList(data)
}
},
)
/**
* @file background.js
*
*/
/**
* 监听popup发来请求信息
* @param message Object
* @param sender
* @param sendResponse function
*/
/**
* 生成message handlers
* @param handlers Object<type: function>
*/
function createMessageHandler(handlers) {
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request?.type && handlers[request.type]) {
handlers[request.type](request.data, sender, sendResponse)
}
return true
})
}
createMessageHandler({
[LIB_ACTION.FETCH_LIST]: (request, sender, sendResponse) => {
try {
const { urlDetail } = request
fetchData().then(
result => {
sendResponse({
data: result,
})
},
error => {
sendResponse({
error,
})
},
)
},
})
长连接请求
发送
chrome.runtime.connect
chrome.tabs.connect
监听该请求发送
chrome.runtime.onConnect
具体看下面来自官方的例
// 发送方
var port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
if (msg.question == "Who's there?")
port.postMessage({answer: "Madame"});
else if (msg.question == "Madame who?")
port.postMessage({answer: "Madame... Bovary"});
});
// 接收方
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "knockknock");
port.onMessage.addListener(function(msg) {
if (msg.joke == "Knock knock")
port.postMessage({question: "Who's there?"});
else if (msg.answer == "Madame")
port.postMessage({question: "Madame who?"});
else if (msg.answer == "Madame... Bovary")
port.postMessage({question: "I don't get it."});
});
});
调试
chrome://extensions/
开启 开发者模式, 并且选择 加载已解压的扩展程序,上传你的开发文件
注意文件根目录下一定要有 manifest.json
background 调试
点击背景页,会弹出 background
的开发者工具,在 background
打日志,或者发送请求时,都能在背景页查看,其生命周期和浏览器一致
popup 调试
右键点击检查,会弹出popup的开发者工具,但是注意,其生命周期和popup的显示隐藏一致,当关闭插件时,其开发者工具也会关闭
插件发布
注意,发布Chrome 需要付费注册开发账号~
chrome.google.com/webstore/de…
注册成功后就可以进入到 Developer Dashboard
注意:
上传图片尽可能多,描述无需关注过多技术用语,直接明了写明用来做什么,为什么需要这些权限
本人的申请曾前后被reject了8次😭😭假如被reject了也没关系~按照reject的说明修改就可以了~祝各位好运🍀
相关链接:
以下是Chrome 官方 Extension 开发文档