企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
参考博客[https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html](https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html) ## **权限对比** | JS种类 | 可访问的API | DOM访问情况 | JS访问情况 | 直接跨域 |调试方式| | --- | --- | --- | --- | --- |--- | | injected script | 和普通JS无任何差别,不能访问任何扩展API | 可以访问 | 可以访问 | 不可以 |F12| | content script | 只能访问 extension、runtime等部分API | 可以访问 | 不可以 | 不可以 |F12的Console中的Top选项![](images/screenshot_1576392961270.png)| | popup js | 可访问绝大部分API,除了devtools系列 | 不可直接访问,但是可以通过chrome.tabs.executeScript来执行脚本 | 不可以 | 可以 |选中插件图标右键审查弹出内容| | background js | 可访问绝大部分API,除了devtools系列 | 不可直接访问,但是可以通过chrome.tabs.executeScript来执行脚本 | 不可以 | 可以 |[chrome://extensions/](chrome://extensions/)中每个插件的背景页| | devtools js | 只能访问 devtools、extension、runtime等部分API | 可以 | 可以 | 不可以 |F12选中插件选项右键审查内容| ## **[消息通信](https://developer.chrome.com/extensions/messaging)** 注:`-`表示不存在或者无意义,或者待验证。 | | injected-script | content-script | popup-js | background-js | | --- | --- | --- | --- | --- | | injected-script | \- | window.postMessage | \- | \- | | content-script | window.postMessage | \- | chrome.runtime.sendMessage chrome.runtime.connect | chrome.runtime.sendMessage chrome.runtime.connect | | popup-js | \- | chrome.tabs.sendMessage chrome.tabs.connect | \- | chrome.extension. getBackgroundPage() | | background-js | \- | chrome.tabs.sendMessage chrome.tabs.connect | chrome.extension.getViews | \- | | devtools-js | chrome.devtools. inspectedWindow.eval | \- | chrome.runtime.sendMessage | chrome.runtime.sendMessage | ### 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 ~~~ > 小插曲,今天碰到一个情况,发现popup无法获取background的任何方法,找了半天才发现是因为background的js报错了,而你如果不主动查看background的js的话,是看不到错误信息的,特此提醒。 至于`background`访问`popup`如下(前提是`popup`已经打开): ~~~ var views = chrome.extension.getViews({type:'popup'}); if(views.length > 0) { console.log(views[0].location.href); } ~~~ ### 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-script.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('我收到了你的消息!'); }); ~~~ 双方通信直接发送的都是JSON对象,不是JSON字符串,所以无需解析,很方便(当然也可以直接发送字符串)。 > 网上有些老代码中用的是`chrome.extension.onMessage`,没有完全查清二者的区别(貌似是别名),但是建议统一使用`chrome.runtime.onMessage`。 ### content-script主动发消息给后台 content-script.js: ~~~ chrome.runtime.sendMessage({greeting: '你好,我是content-script呀,我主动发消息给后台!'}, function(response) { console.log('收到来自后台的回复:' + response); }); ~~~ background.js 或者 popup.js: ~~~ // 监听来自content-script的消息 chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { console.log('收到来自content-script的消息:'); console.log(request, sender, sendResponse); sendResponse('我是后台,我已收到你的消息:' + JSON.stringify(request)); }); ~~~ 注意事项: * content\_scripts向`popup`主动发消息的前提是popup必须打开!否则需要利用background作中转; * 如果background和popup同时监听,那么它们都可以同时收到消息,但是只有一个可以sendResponse,一个先发送了,那么另外一个再发送就无效; ### injected script和content-script `content-script`和页面内的脚本(`injected-script`自然也属于页面内的脚本)之间唯一共享的东西就是页面的DOM元素,有2种方法可以实现二者通讯: 1. 可以通过`window.postMessage`和`window.addEventListener`来实现二者消息通讯; 2. 通过自定义DOM事件来实现; 第一种方法(推荐): `injected-script`中: ~~~ window.postMessage({"test": '你好!'}, '*'); ~~~ content script中: ~~~ window.addEventListener("message", function(e) { console.log(e.data); }, false); ~~~ 第二种方法: `injected-script`中: ~~~ var customEvent = document.createEvent('Event'); customEvent.initEvent('myCustomEvent', true, true); function fireCustomEvent(data) { hiddenDiv = document.getElementById('myCustomEventDiv'); hiddenDiv.innerText = data hiddenDiv.dispatchEvent(customEvent); } fireCustomEvent('你好,我是普通JS!'); ~~~ `content-script.js`中: ~~~ var hiddenDiv = document.getElementById('myCustomEventDiv'); if(!hiddenDiv) { hiddenDiv = document.createElement('div'); hiddenDiv.style.display = 'none'; document.body.appendChild(hiddenDiv); } hiddenDiv.addEventListener('myCustomEvent', function() { var eventData = document.getElementById('myCustomEventDiv').innerText; console.log('收到自定义事件消息:' + eventData); }); ~~~ ## 长连接和短连接 其实上面已经涉及到了,这里再单独说明一下。Chrome插件中有2种通信方式,一个是短连接(`chrome.tabs.sendMessage`和`chrome.runtime.sendMessage`),一个是长连接(`chrome.tabs.connect`和`chrome.runtime.connect`)。 短连接的话就是挤牙膏一样,我发送一下,你收到了再回复一下,如果对方不回复,你只能重新发,而长连接类似`WebSocket`会一直建立连接,双方可以随时互发消息。 短连接上面已经有代码示例了,这里只讲一下长连接。 popup.js: ~~~ getCurrentTabId((tabId) => { var port = chrome.tabs.connect(tabId, {name: 'test-connect'}); port.postMessage({question: '你是谁啊?'}); port.onMessage.addListener(function(msg) { alert('收到消息:'+msg.answer); if(msg.answer && msg.answer.startsWith('我是')) { port.postMessage({question: '哦,原来是你啊!'}); } }); }); ~~~ content-script.js: ~~~ // 监听长连接 chrome.runtime.onConnect.addListener(function(port) { console.log(port); if(port.name == 'test-connect') { port.onMessage.addListener(function(msg) { console.log('收到长连接消息:', msg); if(msg.question == '你是谁啊?') port.postMessage({answer: '我是你爸!'}); }); } }); ~~~