前言
相关文章 谷歌官方文档 (需翻墙)
Chrome 插件开发全攻略 (强烈推荐看这一篇!)
你只需要看完上面那篇文章和掌握一些前端开发基础,就足以自行编写一个 Chrome 插件。本文也是基于上面文章加上自己之前写的插件所记。
什么是 Chrome 插件
如果你用过 Chrome 浏览器的话,也许会用到过一些插件,其中比较知名的就是油猴插件,通过这些插件能够帮你例如自动完成一些功能,屏蔽广告,相当于一个浏览器内置的脚本。应该来说这是 Chrome 扩展开发,不过说 Chrome 插件更顺口,后文也会说成 Chrome 插件。
安装 Chrome 插件
首先打开 Chrome,如下图即可进入插件的管理页面
这时候记得把右上角的开发者模式给勾上,如果不勾上的话你无法直接将文件夹拖入 Chrome 进行安装,就只能安装.crx
格式的文件。Chrome 要求插件必须从它的 Chrome 应用商店(需要翻墙)安装,其它任何网站下载的都无法直接安装,所以可以把crx
文件解压,然后通过开发者模式直接加载。
然后将写好的 Chrome 插件文件夹拖入到刚刚打开的插件管理页面即可。
Chrome 插件知识
manifest.json
是manifest.json
切记不要英文单词打错字,一定要有这个文件,且需要放在根目录上,否则就会出现未能成功加载扩展程序的错误。
background.html 和 background.js
可以理解为后台,同时这个页面会一直常驻在浏览器中,而主要 background 权限非常高,几乎可以调用所有的 Chrome 扩展 API(除了 devtools),基本很多操作都是放在 background 执行,返回给 content,而且它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置CORS
。这对我们后面要在 content 中发送跨域请求至关重要!
我习惯的做法是通过”page”:"background.html"
来导入background.js
或其他 js 代码,如下
// manifest.json
"background": {
"page": "background.html",
},
<!-- background.html -->
<!doctype html>
<html>
<head>
<title>背景页</title>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/background.js"></script>
</body>
</html>
如果是 scripts 方式导入 js 文件则需要反复修改manifest.json
文件。
关于乱码
有时候你在编写代码中出现了中文可能会出现了如下的乱码,
我遇到的原因是就是我原先的background.html
代码写成如下的情况
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/background.js"></script>
没错,就只写了这两个行,就出现乱码(将 UTF-8 的编码变为了 windows1252),而只需要把 background.html 代码修改成正常的 HTML 结构,也就是上上面的那个代码即可解决该乱码情况。
content.js
我们主要的向页面注入脚本就依靠这个文件,相当于给页面添加了一个 js 文件,但是content
和原始页面共享 DOM,但是不共享 JS,如要访问页面 JS(例如某个 JS 变量),只能通过injected js
来实现(后文会提到)。并且content
不能访问绝大部分chrome.xxx.api
,除了下面这 4 种:
- chrome.extension(getURL , inIncognitoContext , lastError , onRequest , sendRequest)
- chrome.i18n
- chrome.runtime(connect , getManifest , getURL , id , onConnect , onMessage , sendMessage)
- chrome.storage
这些 API 绝大部分时候都够用了,非要调用其它 API 的话,你还可以通过通信来实现让 background 来帮你调用。
inject.js
上文也说到了content
是无法访问页面中的 JS,可以操作 DOM,但是 DOM 却不能调用它,也就是无法在 DOM 中通过绑定事件的方式调用content
中的代码(包括直接写onclick
和addEventListener
2 种方式都不行),但是,在页面上添加一个按钮并调用插件的扩展 API是一个很常见的需求,那该怎么办呢?这时候就需要注入 inject.js 这个文件
document.addEventListener('DOMContentLoaded', function () {
injectCustomJs()
})
// 向页面注入JS
function injectCustomJs(jsPath) {
jsPath = jsPath || 'js/inject.js'
var temp = document.createElement('script')
temp.setAttribute('type', 'text/javascript')
// 获得的地址类似:chrome-extension://ihcokhadfjfchaeagdoclpnjdiokfakg/js/inject.js
temp.src = chrome.extension.getURL(jsPath)
temp.onload = function () {
// 放在页面不好看,执行完后移除掉
this.parentNode.removeChild(this)
}
document.head.appendChild(temp)
}
还没有完,因为注入有权限,所以需要在 manifest.json 声明一下这个文件。也就是下面的这行代码
{
// 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的
"web_accessible_resources": ["js/inject.js"],
}
这样你就能调用
关于消息通信
Chrome 插件主要就 4 个部分组成,injected,content,popup,background,但这 4 个部分所对应的权限 ,应用都有可能各自不一,这时候就需要通过消息通信,将对应的数据发送到对应的文件,主要也就如下四种通信方式:
popup 和 background
popup 可以直接调用 background 中的 JS 方法,也可以直接访问 background 的 DOM:
// background.js
function test() {
alert('我是background!')
}
// popup.js
var bg = chrome.extension.getBackgroundPage()
bg.test() // 访问bg的函数
alert(bg.document.body.innerHTML) // 访问bg的DOM
background
访问popup
如下(前提是popup
已经打开):
var views = chrome.extension.getViews({ type: 'popup' })
if (views.length > 0) {
console.log(views[0].location.href)
}
popup 或 bg 与 content
popup 或 bg 向 content 发送请求
//background.js或popup.js:
function sendMessageToContentScript(message, callback) {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, message, function (response) {
if (callback) callback(response)
})
})
}
sendMessageToContentScript({ cmd: 'test', value: '你好,我是popup!' }, function (response) {
console.log('来自content的回复:' + response)
})
content.js
通过监听事件接收:
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
// console.log(sender.tab ?"from a content script:" + sender.tab.url :"from the extension");
if (request.cmd == 'test') alert(request.value)
sendResponse('我收到了你的消息!')
})
content 向 popup 或 bg
// content.js
chrome.runtime.sendMessage(
{ greeting: '你好,我是content呀,我主动发消息给后台!' },
function (response) {
console.log('收到来自后台的回复:' + response)
},
)
//background.js 或 popup.js:
// 监听来自content的消息
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log('收到来自content的消息:')
console.log(request, sender, sendResponse)
sendResponse('我是后台,我已收到你的消息:' + JSON.stringify(request))
})
注意:
- content_scripts 向
popup
主动发消息的前提是 popup 必须打开!否 则需要利用 background 作中转; - 如果 background 和 popup 同时监听,那么它们都可以同时收到消息,但是只有一个可以 sendResponse,一个先发送了,那么另外一个再发送就无效;
injected 和 content
主要就是injected
向content
发送,injected
无需监听。
content
和页面内的脚本(injected
自然也属于页面内的脚本)之间唯一共享的东西就是页面的 DOM 元素,有 2 种方法可以实现二者通讯,:
- 可以通过
window.postMessage
和window.addEventListener
来实现二者消息通讯;(推荐) - 通过自定义 DOM 事件来实现(我就懒得写了,没怎么用到);
injected
中:
window.postMessage({ test: '你好!' }, '*')
content script中
:
window.addEventListener(
'message',
function (e) {
console.log(e.data)
},
false,
)
injected 与 popup
injected
无法直接和popup
通信,必须借助content
作为中间人。不过一般这种都少,直接和 bg 通信即可。
我的模板
关于 Chrome 的主要内容也就这些,实际开发如果有个模板就能大大方便开发,在原文章中该作者已经分享了有对应的源代码,这里放上我自写的 Chrome 模板编写过程。
当然,这里需要提几点地方: