文章目录
crm开发定制这里先插播一条消息
Manifest version 2 is deprecated, and support will be removed in 2023. See https://developer.chrome.com/blog/mv2-transition/ for more details.
MV2版本的在2023crm开发定制年停止支持
chromecrm开发定制插件应该包含哪些文件及文件夹
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- html:存放html页面
- images:crm开发定制存放插件图标
- scripts:存放js文件
- styles:存放样式
- _locales:crm开发定制存放多语言文件
- manifest.json:crm开发定制用来配置所有和插件相关的配置,作为chrome入口文件,crm开发定制必须放在根目录(
必须存在
)
分析
- crm开发定制目录结构像一个web网页,crm开发定制他的本质上就是一个网站类应用,是一个webapp
- crm开发定制相对于普通的webapp,crm开发定制还可以调用更多的浏览器层面的api,包括数钱、历史记录、crm开发定制网络请求拦截、crm开发定制截获用户输入等等
crm开发定制重要配置说明
manifest.json
crm开发定制额外的配置参见
{ "manifest_version": 3, // crm开发定制清单版本号,建议使用 版本 3,版本 1和2 是旧的,已弃用,crm开发定制不建议使用 "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": ["<all_urls>"] // 被植入到页面,只在这些站点下 content_scripts会运行 } ], "permissions": [ // 安装的时候提示㤇的权限 "cookies", // 使用cookies "webRequest", // 使用web请求 "http://*", // 可以通过executeScript或者insertCSS访问的网站地址。如: https://*.google.com/ "management", // "storage", // 使用本地存储 "tabs", // 操作标签 "contextMenus" //右键菜单 ] "default_locale ": "zh_CN" //默认语言(比如"zh_CN")}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
开始手撸一个插件
准备工作
创建一个文件夹,如我的叫 extensions (之后说的根目录都是指这个目录下)
文件夹下创建一个 img 目录,用于存放一些logo之类的图片
放入一张图片,如logo.png
文件夹下创建一个 html 目录,用于存放html文件
文件夹下创建一个 js 目录,用于存放js文件
这里如果你想先放一个jquery
文件用于加载也是可以的,我后面为了方便使用的是script
引入
文件夹下创建一个 css 目录,用于存放css文件
文件夹根目录下创建一个 manifest.json 文件
{ "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" }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
然后chrome扩展程序【加载已解压的扩展程序】选择刚才创建的extensions
目录
效果如下
pupup部分
-
在
/html
新建一个popup.html
文件,然后在manifest.json
中的action
配置popup
的路径"action":{ "default_title":"这是鼠标移上去时提示文字", "default_popup":"html/popup.html"}
- 1
- 2
- 3
- 4
<!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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
在css和js目录中分别新建
popup.css
和popup.js
文件/* popup.css */.btn{ width: 100px; height: 30px; font-size: large;}
- 1
- 2
- 3
- 4
- 5
- 6
//popup.js$(".checkbtn").click(function(){ alert($(this).attr('id'));});
- 1
- 2
- 3
- 4
-
然后重载扩展程序
点击插件,效果如下
-
待解决问题
每次勾选的checkbox都会被还原,所以接下来需要做一个本地存储来保存的改变
background部分
-
在
manifest.json
中加入service_worker
的配置路径和permissions
"background":{ "service_worker":"background.js"},"permissions":["storage"]
- 1
- 2
- 3
- 4
注意:
service_worker
说明- 这个是一直伴随插件运行的后台脚本
- 没有前端页面,不支持dom,所以不能引入jQuery和其他js
- 所有需要保持运行的脚本都需要直接卸载
background.js
文件里 - 同样也不支持
XMLHttpRequest
,所以需要使用fetch
来替代xhr请求 - 一定要放在
根目录
(扩展文件根目录,不是电脑磁盘根目录),否则在使用的时候会出现service worker(无效)
提示 - 可以在
扩展程序
=>查看视图
点击弹出的控制台查看输出
-
在根目录写
background.js
文件//background.jschrome.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(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
-
打开
popup.js
,把原来的点击事件删掉,在其中加入初始化和连接service_worker的脚本//popup.jswindow.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}); } });});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
-
打开
background.js
,在其中加入监听popup的脚本(这里不要删除原来的哦,加到后面即可)//background.jschrome.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) } })});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
-
重载插件
-
这个时候会发现有两个报错
发现是用
script
引入的jquery
报那么就改为文件引入呗
-
在js目录下新建
jquery.js
到去下载
production
版本的js然后把他的内容放到
jquery.js
中 -
修改
popup.html
文件中的jquery引入<script src="../js/jquery.js"></script>
- 1
-
-
重载插件,发现报错都好了
-
-
测试
每次重置勾选的问题已经好了
content部分
content可以注入到浏览的网页,操作dom,所以就可以实现很多功能了
manifest.json
中加入content
的配置
"content_scripts":[{ "js":["js/jquery.js","js/content.js"],/*content可以随意引入js,因为其内容会在浏览的网页上直接运行*/ "matches":["*://localhost/*"],/*在哪些网页上运行*/ "run_at":"document_end"/* 在页面加载完成时运行 */}]
- 1
- 2
- 3
- 4
- 5
注意这里现在只是匹配的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返回值');});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
-
然后
background.js
中加入监听content
的代码//background.jschrome.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; } } }); }});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
这里注意
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>
- 1
- 2
- 3
- 4
- 5
- 6
-
在
popup.html
中增加一个去广告按钮
<div class="btn"> 去广告<input id="removeAD" class="checkbtn" type="checkbox" /></div>
- 1
- 2
- 3
-
在
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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
-
在
content.js
中监听background.js
部分增加removeAD
的判断case 'removeAD': //隐藏含有ad的元素,来达到去广告的效果 $(".ad").hide(); break;
- 1
- 2
- 3
- 4
-
重载插件,勾选页面中的
去广告
,然后刷新页面,发现广告已经没有了
页面跳转和cookie
和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"}]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
这里千万要注意的是一定要能够匹配上的url,否则可能引起
页面脚本无反应
或者获取不到cookie
-
在
popup.html
中增加按钮<div class="btn"> csdn<input id="checkCsdnUserNick" class="checkbtn" type="checkbox" /></div>
- 1
- 2
- 3
-
在
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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
-
在
content.js
中增加对于csdn按钮
的判断case 'checkCsdnUserNick': if(LocalDB.step==2){ $("body").append('<h1>'+LocalDB.cookie+'</h1>'); } break;
- 1
- 2
- 3
- 4
- 5
-
重载插件,到
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
进行返回可以参见
-
-
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
是否完全正确
-