Manifest version 2 is deprecated, and support will be removed in 2023. See https://developer.chrome.com/blog/mv2-transition/ for more details.
MV2版本的chrome插件在2023年停止支持
D:.
│ manifest.json
│
├─html
│ index.html
│
├─images
│ icon-128.png
│ icon-16.png
│
├─scripts
│ background.js
│
├─styles
│ main.css
│
└─_locales
├─en
│ messages.json
│
└─zh_CN
messages.json
必须存在)分析
manifest.json
额外的配置参见https://blog.csdn.net/sysuzjz/article/details/51648163
{
"manifest_version": 3, // 清单版本号,建议使用 版本 3,版本 1和2 是旧的,已弃用,不建议使用
"name": "first-test-plugin", // 插件名称
"version": "0.0.1", // 插件版本
"description": "这里是第一个测试插件", // 描述,可写可不写
"icons":
{
"16": "images/custom/16x16.png",
"48": "images/custom/48x48.png",
"128": "images/custom/128x128.png"
},
// !!!browser_action和page_action只能添加一个
"browser_action": //浏览器级别行为,所有页面均生效
{
"default_icon": "images/custom/16x16.png", // 图标的图片
"default_title": "Hello lanfengqiuqian", // 鼠标移到图标显示的文字
"default_popup": "html/popup.html" // 单击图标后弹窗页面
},
"page_action": //页面级别的行为,只在特定页面下生效
{
"default_icon":
{
"24": "images/custom/24x24.png",
"38": "images/custom/38x38.png"
},
"default_popup": "html/popup.html",
"default_title": "Hello lanfengqiuqian"
},
"author": "lanfengqiuqian", // 可选填写
"automation": false, // 是否开启自动化
"background": // 背景页的脚本路径,一般为插件目录的相对地址
{
"scripts": [
"scripts/background.js",
"scripts/devtools-page.js"
]
},
"devtools_page": "html/devtools-page.html", // 在开发工具中的页面
"content_scripts": [ // 内容脚本一般植入会被植入到页面中, 并且可以控制页面中的dom
{
"js": ["js/else-insert.js"], // 这里面的数组都是可以放多个的
"css": ["css/else-insert.css"],
"matches": ["" ] // 被植入到页面,只在这些站点下 content_scripts会运行
}
],
"permissions": [ // 安装的时候提示㤇的权限
"cookies", // 使用cookies
"webRequest", // 使用web请求
"http://*", // 可以通过executeScript或者insertCSS访问的网站地址。如: https://*.google.com/
"management", //
"storage", // 使用本地存储
"tabs", // 操作标签
"contextMenus" //右键菜单
]
"default_locale ": "zh_CN" //默认语言(比如"zh_CN")
}
放入一张图片,如logo.png
这里如果你想先放一个jquery文件用于加载也是可以的,我后面为了方便使用的是script引入
{
"manifest_version":3,
"name":"这是插件名称",
"version":"0.0.1",
"description":"这是插件描述",
"action":{
"default_title":"这是鼠标移上去时提示文字",
"default_popup":"html/popup.html"
},
"icons":{
"16":"img/logo.png",
"32":"img/logo.png",
"48":"img/logo.png",
"128":"img/logo.png"
}
}
然后chrome扩展程序【加载已解压的扩展程序】选择刚才创建的extensions目录
效果如下
在/html新建一个popup.html文件,然后在manifest.json中的action配置popup的路径
"action":{
"default_title":"这是鼠标移上去时提示文字",
"default_popup":"html/popup.html"
}
DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="../css/popup.css" />
head>
<body>
<div class="btn">
测试<input id="TEST" class="checkbtn" type="checkbox" />
div>
body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js">script>
<script src="../js/popup.js">script>
html>
在css和js目录中分别新建popup.css和popup.js文件
/* popup.css */
.btn{
width: 100px;
height: 30px;
font-size: large;
}
//popup.js
$(".checkbtn").click(function(){
alert($(this).attr('id'));
});
然后重载扩展程序
点击插件,效果如下
待解决问题
每次勾选的checkbox都会被还原,所以接下来需要做一个本地存储来保存popup的改变
在manifest.json中加入service_worker的配置路径和permissions
"background":{
"service_worker":"background.js"
},
"permissions":["storage"]
注意:service_worker说明
background.js文件里XMLHttpRequest,所以需要使用fetch来替代xhr请求根目录(扩展文件根目录,不是电脑磁盘根目录),否则在使用的时候会出现service worker(无效)提示扩展程序=>查看视图点击弹出的控制台查看输出在根目录写background.js文件
//background.js
chrome.runtime.onInstalled.addListener(() => {
DBdata("clear");//清除插件保存的本地数据
});
//插件用的数据都存储在storage.local中
function DBdata(mode,callback,data){//操作本地存储的函数
if(mode=="set"){//保存本地数据
console.log('set-LocalDB');
chrome.storage.local.set({LocalDB: data});
}else if(mode=="get"){//获取
chrome.storage.local.get('LocalDB', function(response) {
typeof callback == 'function' ? callback(response) : null;
});
}else if(mode=="clear"){//清空
chrome.storage.local.clear();
}
}
打开popup.js,把原来的点击事件删掉,在其中加入初始化和连接service_worker的脚本
//popup.js
window.bgCommunicationPort = chrome.runtime.connect();//初始化bgCommunicationPort
$(".checkbtn").click(function(){
bgCommunicationPort.postMessage({//发送到bg,键值可以自由设置
Direct : $(this).attr('id'),//目标
Content : '测试内容',//内容
step : 0//步骤
});
});
$(document).ready(function(){//打开popup时触发,读取之前存储的参数
bgCommunicationPort.postMessage({fromPopup:'getDB'});//向background发送消息
bgCommunicationPort.onMessage.addListener(function(receivedPortMsg) {//监听background
console.log(receivedPortMsg);//这是background发来的内容
if(receivedPortMsg&&receivedPortMsg.Direct){
$(".checkbtn").prop({'checked': false});//初始化按钮
$("#"+receivedPortMsg.Direct).prop({'checked': true});
}
});
});
打开background.js,在其中加入监听popup的脚本(这里不要删除原来的哦,加到后面即可)
//background.js
chrome.runtime.onConnect.addListener(function(port) {//接收到popup
port.onMessage.addListener(function(receivedMsg) {//监听popup发来的内容receivedMsg
if(receivedMsg.fromPopup&&receivedMsg.fromPopup=='getDB'){//如果接收到了getDB,这里读取数据并返回相当于初始化popup页面
DBdata('get',function(res){
port.postMessage(res.LocalDB);//发送到popup
});
}else{//如果不是,则说明是收到来自popup手动点击设置的数据,存入以用于popup打开时展示
DBdata('set','',receivedMsg)
}
})
});
重载插件
这个时候会发现有两个报错
发现是用script引入的jquery报跨域
那么就改为文件引入呗
在js目录下新建jquery.js
到https://jquery.com/download/去下载production版本的js
然后把他的内容放到jquery.js中
修改popup.html文件中的jquery引入
<script src="../js/jquery.js">script>
重载插件,发现报错都好了
测试
每次重置勾选的问题已经好了
content可以注入到浏览的网页,操作dom,所以就可以实现很多功能了
manifest.json中加入content的配置"content_scripts":[{
"js":["js/jquery.js","js/content.js"],/*content可以随意引入js,因为其内容会在浏览的网页上直接运行*/
"matches":["*://localhost/*"],/*在哪些网页上运行*/
"run_at":"document_end"/* 在页面加载完成时运行 */
}]
注意这里现在只是匹配的localhost哦
新建js/content.js,在content.js中写入
//content.js manifest匹配地址的页面在刷新时会直接执行这里的代码
chrome.runtime.sendMessage(chrome.runtime.id, {//当页面刷新时发送到bg
fromContent: 'getDB'
});
chrome.runtime.onMessage.addListener(function(senderRequest, sender, sendResponse) {//接收到bg
console.log('demo已运行');
var LocalDB=senderRequest.LocalDB;
console.log(LocalDB);
switch(LocalDB.Direct){
case 'TEST':
console.log(123123);
break;
default:
break;
}
// 不写会报错 Unchecked runtime.lastError: The message port closed before a response was received.
sendResponse('这里是content返回值');
});
然后background.js中加入监听content的代码
//background.js
chrome.runtime.onMessage.addListener(function (senderRequest, sender, sendResponse) {//接收到content
// 不写会报错 Unchecked runtime.lastError: The message port closed before a response was received.
sendResponse({ msg: '接收到content' });
console.log(senderRequest);
if (senderRequest.fromContent && senderRequest.fromContent == 'getDB') {//接收到fromContent:getDB
DBdata('get', function (res) {//从本地取数据
if (res.LocalDB) {
var LocalDB = res.LocalDB;
switch (LocalDB.Direct) {
//如果是存入的TEST按钮
case 'TEST':
chrome.tabs.query({
active: true,
currentWindow: true
}, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { LocalDB: LocalDB }, function (res) {
console.log('接收content的回调', res);
});//发送到content
});
break;
default:
break;
}
}
});
}
});
这里注意sendResponse这个方法,有的没有在回调中加上这个参数,导致找不到
重载插件
代码执行顺序
插件初始化阶段
background.js中onInstalled,清除本地数据手动点击插件图标,勾选插件
popue.js的ready方法进行初始化click方法修改本地数据网页刷新阶段
content.js的sendMessage方法background.js的onMessage方法sendMessage到content说明:这里对于广告的判断是类名为 .ad 的元素
如我的vue页面
<div>
<h2 class="ad">第一条广告h2>
<h2 class="ad">第二条广告h2>
<h2 class="ad">第三条广告h2>
<h2>这里是正常的数据h2>
div>
在popup.html中增加一个去广告按钮
<div class="btn">
去广告<input id="removeAD" class="checkbtn" type="checkbox" />
div>
在background.js中监听content部分增加对于removeAD的判断
//如果是存入的removeAD按钮
case 'removeAD':
chrome.tabs.query({active: true, currentWindow: true
}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {LocalDB: LocalDB});//发送到content
});
break;
在content.js中监听background.js部分增加removeAD的判断
case 'removeAD':
//隐藏含有ad的元素,来达到去广告的效果
$(".ad").hide();
break;
重载插件,勾选页面中的去广告,然后刷新页面,发现广告已经没有了
和popup一样,content关闭之后也不会保存数据,当我们在A页面获取数据之后想要放到B页面上去,在content直接跳转是不会把获取到的数据传过去的,所以和background链接保存本地数据就派上用场了
案例:将csdn的cookie的UserNick显示在localhost:8081中
在manifest.json中配置【域名脚本匹配】、【权限】和【主机权限】
"permissions":["storage", "cookies"],
"host_permissions": [
"*://www.csdn.net/*"
],
"content_scripts":[{
"js":["js/jquery.js","js/content.js"],
"matches":["*://localhost/*", "*://www.csdn.net/*"],
"run_at":"document_end"
}]
这里千万要注意的是一定要能够匹配上的url,否则可能引起页面脚本无反应或者获取不到cookie
在popup.html中增加按钮
<div class="btn">
csdn<input id="checkCsdnUserNick" class="checkbtn" type="checkbox" />
div>
在background.js中增加对于csdn按钮的判断
case 'checkCsdnUserNick':
console.log('LocalDB', LocalDB)
//popup设置数据的时候有个step属性,在多步操作的时候就开始发挥作用了
if(LocalDB.step==0){
LocalDB.step = 1;//将step设置成1
chrome.storage.local.set({
LocalDB: LocalDB//保存到本地数据
},function() {
chrome.tabs.update(null, {//将前台页面跳转到设置的url
// 这里的url不用带斜杠 /
url: 'https://www.csdn.net'
});
});
}else if(LocalDB.step==1){//因为csdn的地址我们也匹配了所以content在跳转到csdn之后会还是会回来,不同的是step已经是1了
chrome.cookies.get({//获取cookie
'url': "https://www.csdn.net",
'name': 'UserNick'
}, function(cookie) {
console.log('cookie', cookie);
console.log(cookie.value);//获取到的值
LocalDB.cookie=cookie.value;//把获取到的值放到本地数据的cookie属性里
LocalDB.step = 2;//将step设置成2
chrome.storage.local.set({//获取到cookie之后跳转到第二个页面
LocalDB: LocalDB//保存到本地数据
},function() {
chrome.tabs.update(null, {//将前台页面跳转到设置的url
url: 'http://localhost:8081/'
});
});
});
}else if(LocalDB.step==2){//第二步
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){//发送到content
chrome.tabs.sendMessage(tabs[0].id, {LocalDB: LocalDB});
});
}
break;
在content.js中增加对于csdn按钮的判断
case 'checkCsdnUserNick':
if(LocalDB.step==2){
$("body").append(''
+LocalDB.cookie+'');
}
break;
重载插件,到csdn.net中开启插件,勾选csdn,然后刷新页面就能看到效果
会获取csdn.net中cookie中的昵称,然后跳转到localhost:8081,进行显示
Unchecked runtime.lastError: The message port closed before a response was received.
这个问题通常是由于其他插件引起的,注意排查,找到受影响的插件禁用即可
大多数人是由于【迅雷】插件或者【油猴】插件引起的
扩展【移除】旁边多了一个【错误】的按钮
如果有错误提示,根据提示排查即可
如果没有错误提示,尝试将扩展移除重新加载即可
检查是否没有把sendMessage和sendResponse配套使用
每一个sendMessage都需要和sendResponse进行呼应
也就是说,在每一个chrome.runtime.onMessage.addListener的回调函数中,需要使用sendResponse进行返回
可以参见https://blog.csdn.net/m0_37729058/article/details/89186257
service worker看不到content.js的console.log
原因是因为这个js是嵌入到页面中的,所以需要在使用的网页的控制台查看,而不是service worker
无法获取到cookie
可能原因如下
没有授权host_permissions,控制台会报错Unchecked runtime.lastError: No host permissions for cookies at url: "https://www.csdn.net/".
检查manifest.json中的此项配置,没有添加或者没有匹配上都会造成这个问题
检查chrome.cookies.get这个方法中的url是否完全正确