Chrome插件-踩坑
本篇是对开发Chrome插件开发过程中一些功能实现所需到的问题进行梳理总结,如:文件的通信、插件图标和badgeText的动态设置,等等
插件只在指定的网页启用
“The Declarative Content API allows you to enable your extension's action depending on the URL of a web page, or if a CSS selector matches an element on the page, without needing to add host permissions or inject a content script."
通过Declarative
设置URL或者CSS选择器匹配规则,可以实现在特定的网页开启插件功能。
首先,在manifest.json中声明permissions
和指定page_action
。注意:不是browser_action
,它是针对所有页面都有效的一种设置图标的方式,不支持指定URL有效。
{
"page_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/icon_16.png",
"32": "images/icon_32.png",
"48": "images/icon_48.png",
"128": "images/icon_128.png"
}
},
"permissions": [
"declarativeContent"
]
}
然后,在background.js
中调用chrome.declarativeContent
API进行控制。
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [new chrome.declarativeContent.PageStateMatcher({
pageUrl: {hostEquals: 'developer.chrome.com'},
})
],
actions: [new chrome.declarativeContent.ShowPageAction()]
}]);
});
});
通过pageUrl控制
通过CSS选择器控制
完整使用参考 文档-chrome.declarativeContent
BrowserAction相关操作
- 设置badgeText
function updateBadgeText(text = '', color = "#f50000") {
chrome.browserAction.setBadgeText({ text: text });
chrome.browserAction.setBadgeBackgroundColor({
color: color,
});
}
- 修改icon
function updateActionIcon(iconPath) {
chrome.browserAction.setIcon({
path: iconPath,
});
}
浏览器消息气泡通知
function sendNotifications(title, message, icon) {
chrome.notifications.create(null, {
type: "basic",
iconUrl: icon || "static/img/logo.png",
title: title,
message: message,
});
}
不同文件访问Chrome扩展API的限制
content_scripts
作为可注入到运行页面的脚本(还有CSS),可访问的API最少,常用的有下几个: runtime、storage、extension、i18n
。 另外要注意的是,content-scripts
和运行页面共享DOM
,但是不共享js
,如要访问页面js
(例如某个js
变量),只能通过injected js
来实现。
background
是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。
background的权限非常高,几乎可以调用所有的Chrome扩展API,而且它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置CORS。
popup
它和background非常类似,它们之间最大的不同是生命周期的不同,popup中可以直接通过chrome.extension.getBackgroundPage()获取background的window对象。
通信 💬说 👂听
场景 content_script.js
💬 popup.js
👂
通常情况下,我们在content_script编写插件主代码,运行的临时数据都在这类js里缓存,当点开popup页面后,如何拿到这些数据,对UI进行更新,这里就需要用到两者通信了。
// content_script.js 向popup发送一条消息:下载进度
chrome.runtime.sendMessage(
{
cmd: "onprogress",
value: {
domId,
progress,
},
},
function (response) {
console.log('接收到popup.js的[onprogress]事件应答:',response);
}
);
popup
通过chrome.runtime.onMessage.addListener
接收消息,request
是消息体,这里用request.cmd
来区分不同的消息类型。
// popup.js 接收
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
const { value, cmd } = request;
const { domId, progress } = value || {};
if (!domId) {
return;
}
if (cmd == "onprogress") {
updateDownloadBtnUI(domId, "progress", progress);
}
if (cmd == "finished") {
updateDownloadBtnUI(domId, "finished", 100);
}
if (cmd == "failed") {
updateDownloadBtnUI(domId, "failed", 0);
}
sendResponse(
"我是popup,我已收到content_sript的事件消息:" + JSON.stringify(request)
);
});
场景 content_script.js💬 background.js👂
基本上和
content_script.js
与popup.js
通信方式一致。
场景 popup.js💬 content_script.js👂
发送方
popup.js
获取当前tab
,调用chrome.tabs.sendMessage
发送消息,接收方content_script.js
依然通过chrome.runtime.onMessage.addListener
接收消息。
// 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);
});
});
}
// 调用
testBtnDom.addEventListener("click", function () {
sendMessageToContentScript(
{ cmd: "test", value: "show me the test code" },
function (res) {
console.log(res);
}
);
});
//content_script.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.cmd == "test") {
console.log("content_script.state:", state);
sendResponse({
msg: "hello there",
});
}
return true;
});
场景 popup.js
与background.js
通信
在
popup.js
中调用background.js
中的方法。实际上是通过chrome.extension.getBackgroundPage()获取background的window对象。
var bg = chrome.extension.getBackgroundPage();
bg.doSomething(); //doSomething()是background中的一个方法
V2迁移V3
两个版本差异点还是挺多的,总的来说,对目前大部分插件都有影响,需要大量兼容。以下列举一些本人遇到问题。
全面禁止evel
V3无法再通过evel
的方式运行代码字符串了,所有动态的字符串代码必须保存到指定的js
文件中,并在manifest.json
注册表中指定新的background
,以前的background.scripts
改为background.service_worker
,且值为字符串。
{
"background": {
"service_worker": "background.js"
}
}
manifest.json
改动
browser_actionor
或者page_action
,统一改为action
。 *background.scripts
改为background.service_worker
。且service_worker字段采用字符串,而不是字符串数组。(service_worker
必须在根级别注册:它们不能在嵌套目录中。)。- 在
popup.js
中调用background.js
中的方法,从之前的chrome.runtime.getBackgroundPage()
改为 background迁移。 - web_accessible_resources的值从之前的资源路径字符串数组改为对象数组,且每个对象配置至少指定两个字段。
// V3
"web_accessible_resources": [{
"resources": [RESOURCE_PATHS],
"matches": [MATCH_PATTERNS],
"extension_ids": [EXTENSION_IDS],
optional "use_dynamic_url": boolean
}]