NIUCLOUD是一款SaaS管理后台框架多应用插件+云编译。上千名开发者、服务商正在积极拥抱开发者生态。欢迎开发者们免费入驻。一起助力发展! 广告
# 5种类型的JS对比及消息通信 参考博客<https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html> ## **权限对比** JS种类可访问的APIDOM访问情况JS访问情况直接跨域调试方式injected script和普通JS无任何差别,不能访问任何扩展API可以访问可以访问不可以F12content script只能访问 extension、runtime等部分API可以访问不可以不可以F12的Console中的Top选项![](https://img.kancloud.cn/cd/ca/cdcade21b050fe06348ff962fdfbd44a_261x55.png)popup js可访问绝大部分API,除了devtools系列不可直接访问,但是可以通过chrome.tabs.executeScript来执行脚本不可以可以选中插件图标右键审查弹出内容background js可访问绝大部分API,除了devtools系列不可直接访问,但是可以通过chrome.tabs.executeScript来执行脚本不可以可以<chrome://extensions/>中每个插件的背景页devtools js只能访问 devtools、extension、runtime等部分API可以可以不可以F12选中插件选项右键审查内容## **[消息通信](https://developer.chrome.com/extensions/messaging)** 注:`-`表示不存在或者无意义,或者待验证。 injected-scriptcontent-scriptpopup-jsbackground-jsinjected-script-window.postMessage--content-scriptwindow.postMessage-chrome.runtime.sendMessage chrome.runtime.connectchrome.runtime.sendMessage chrome.runtime.connectpopup-js-chrome.tabs.sendMessage chrome.tabs.connect-chrome.extension. getBackgroundPage()background-js-chrome.tabs.sendMessage chrome.tabs.connectchrome.extension.getViews-devtools-jschrome.devtools. inspectedWindow.eval-chrome.runtime.sendMessagechrome.runtime.sendMessage### popup和background popup可以直接调用background中的JS方法,也可以直接访问background的DOM: ``` <pre class="calibre10">``` <span class="token">// background.js</span> <span class="token5">function</span> <span class="token4">test</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">alert</span><span class="token3">(</span><span class="token2">'我是background!'</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">// popup.js</span> var bg <span class="token1">=</span> chrome<span class="token3">.</span>extension<span class="token3">.</span><span class="token4">getBackgroundPage</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> bg<span class="token3">.</span><span class="token4">test</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">// 访问bg的函数</span> <span class="token4">alert</span><span class="token3">(</span>bg<span class="token3">.</span>document<span class="token3">.</span>body<span class="token3">.</span>innerHTML<span class="token3">)</span><span class="token3">;</span> <span class="token">// 访问bg的DOM</span> ``` ``` > 小插曲,今天碰到一个情况,发现popup无法获取background的任何方法,找了半天才发现是因为background的js报错了,而你如果不主动查看background的js的话,是看不到错误信息的,特此提醒。 至于`background`访问`popup`如下(前提是`popup`已经打开): ``` <pre class="calibre10">``` var views <span class="token1">=</span> chrome<span class="token3">.</span>extension<span class="token3">.</span><span class="token4">getViews</span><span class="token3">(</span><span class="token3">{</span>type<span class="token3">:</span><span class="token2">'popup'</span><span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> <span class="token5">if</span><span class="token3">(</span>views<span class="token3">.</span>length <span class="token1">></span> <span class="token6">0</span><span class="token3">)</span> <span class="token3">{</span> console<span class="token3">.</span><span class="token4">log</span><span class="token3">(</span>views<span class="token3">[</span><span class="token6">0</span><span class="token3">]</span><span class="token3">.</span>location<span class="token3">.</span>href<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> ``` ``` ### popup或者bg向content主动发送消息 background.js或者popup.js: ``` <pre class="calibre10">``` <span class="token5">function</span> <span class="token4">sendMessageToContentScript</span><span class="token3">(</span>message<span class="token3">,</span> callback<span class="token3">)</span> <span class="token3">{</span> chrome<span class="token3">.</span>tabs<span class="token3">.</span><span class="token4">query</span><span class="token3">(</span><span class="token3">{</span>active<span class="token3">:</span> <span class="token6">true</span><span class="token3">,</span> currentWindow<span class="token3">:</span> <span class="token6">true</span><span class="token3">}</span><span class="token3">,</span> <span class="token5">function</span><span class="token3">(</span>tabs<span class="token3">)</span> <span class="token3">{</span> chrome<span class="token3">.</span>tabs<span class="token3">.</span><span class="token4">sendMessage</span><span class="token3">(</span>tabs<span class="token3">[</span><span class="token6">0</span><span class="token3">]</span><span class="token3">.</span>id<span class="token3">,</span> message<span class="token3">,</span> <span class="token5">function</span><span class="token3">(</span>response<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span><span class="token3">(</span>callback<span class="token3">)</span> <span class="token4">callback</span><span class="token3">(</span>response<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token4">sendMessageToContentScript</span><span class="token3">(</span><span class="token3">{</span>cmd<span class="token3">:</span><span class="token2">'test'</span><span class="token3">,</span> value<span class="token3">:</span><span class="token2">'你好,我是popup!'</span><span class="token3">}</span><span class="token3">,</span> <span class="token5">function</span><span class="token3">(</span>response<span class="token3">)</span> <span class="token3">{</span> console<span class="token3">.</span><span class="token4">log</span><span class="token3">(</span><span class="token2">'来自content的回复:'</span><span class="token1">+</span>response<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> ``` ``` `content-script.js`接收: ``` <pre class="calibre10">``` chrome<span class="token3">.</span>runtime<span class="token3">.</span>onMessage<span class="token3">.</span><span class="token4">addListener</span><span class="token3">(</span><span class="token5">function</span><span class="token3">(</span>request<span class="token3">,</span> sender<span class="token3">,</span> sendResponse<span class="token3">)</span> <span class="token3">{</span> <span class="token">// console.log(sender.tab ?"from a content script:" + sender.tab.url :"from the extension");</span> <span class="token5">if</span><span class="token3">(</span>request<span class="token3">.</span>cmd <span class="token1">==</span> <span class="token2">'test'</span><span class="token3">)</span> <span class="token4">alert</span><span class="token3">(</span>request<span class="token3">.</span>value<span class="token3">)</span><span class="token3">;</span> <span class="token4">sendResponse</span><span class="token3">(</span><span class="token2">'我收到了你的消息!'</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> ``` ``` 双方通信直接发送的都是JSON对象,不是JSON字符串,所以无需解析,很方便(当然也可以直接发送字符串)。 > 网上有些老代码中用的是`chrome.extension.onMessage`,没有完全查清二者的区别(貌似是别名),但是建议统一使用`chrome.runtime.onMessage`。 ### content-script主动发消息给后台 content-script.js: ``` <pre class="calibre10">``` chrome<span class="token3">.</span>runtime<span class="token3">.</span><span class="token4">sendMessage</span><span class="token3">(</span><span class="token3">{</span>greeting<span class="token3">:</span> <span class="token2">'你好,我是content-script呀,我主动发消息给后台!'</span><span class="token3">}</span><span class="token3">,</span> <span class="token5">function</span><span class="token3">(</span>response<span class="token3">)</span> <span class="token3">{</span> console<span class="token3">.</span><span class="token4">log</span><span class="token3">(</span><span class="token2">'收到来自后台的回复:'</span> <span class="token1">+</span> response<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> ``` ``` background.js 或者 popup.js: ``` <pre class="calibre10">``` <span class="token">// 监听来自content-script的消息</span> chrome<span class="token3">.</span>runtime<span class="token3">.</span>onMessage<span class="token3">.</span><span class="token4">addListener</span><span class="token3">(</span><span class="token5">function</span><span class="token3">(</span>request<span class="token3">,</span> sender<span class="token3">,</span> sendResponse<span class="token3">)</span> <span class="token3">{</span> console<span class="token3">.</span><span class="token4">log</span><span class="token3">(</span><span class="token2">'收到来自content-script的消息:'</span><span class="token3">)</span><span class="token3">;</span> console<span class="token3">.</span><span class="token4">log</span><span class="token3">(</span>request<span class="token3">,</span> sender<span class="token3">,</span> sendResponse<span class="token3">)</span><span class="token3">;</span> <span class="token4">sendResponse</span><span class="token3">(</span><span class="token2">'我是后台,我已收到你的消息:'</span> <span class="token1">+</span> JSON<span class="token3">.</span><span class="token4">stringify</span><span class="token3">(</span>request<span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> ``` ``` 注意事项: - 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`中: ``` <pre class="calibre10">``` window<span class="token3">.</span><span class="token4">postMessage</span><span class="token3">(</span><span class="token3">{</span><span class="token2">"test"</span><span class="token3">:</span> <span class="token2">'你好!'</span><span class="token3">}</span><span class="token3">,</span> <span class="token2">'*'</span><span class="token3">)</span><span class="token3">;</span> ``` ``` content script中: ``` <pre class="calibre10">``` window<span class="token3">.</span><span class="token4">addEventListener</span><span class="token3">(</span><span class="token2">"message"</span><span class="token3">,</span> <span class="token5">function</span><span class="token3">(</span>e<span class="token3">)</span> <span class="token3">{</span> console<span class="token3">.</span><span class="token4">log</span><span class="token3">(</span>e<span class="token3">.</span>data<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">,</span> <span class="token6">false</span><span class="token3">)</span><span class="token3">;</span> ``` ``` 第二种方法: `injected-script`中: ``` <pre class="calibre10">``` var customEvent <span class="token1">=</span> document<span class="token3">.</span><span class="token4">createEvent</span><span class="token3">(</span><span class="token2">'Event'</span><span class="token3">)</span><span class="token3">;</span> customEvent<span class="token3">.</span><span class="token4">initEvent</span><span class="token3">(</span><span class="token2">'myCustomEvent'</span><span class="token3">,</span> <span class="token6">true</span><span class="token3">,</span> <span class="token6">true</span><span class="token3">)</span><span class="token3">;</span> <span class="token5">function</span> <span class="token4">fireCustomEvent</span><span class="token3">(</span>data<span class="token3">)</span> <span class="token3">{</span> hiddenDiv <span class="token1">=</span> document<span class="token3">.</span><span class="token4">getElementById</span><span class="token3">(</span><span class="token2">'myCustomEventDiv'</span><span class="token3">)</span><span class="token3">;</span> hiddenDiv<span class="token3">.</span>innerText <span class="token1">=</span> data hiddenDiv<span class="token3">.</span><span class="token4">dispatchEvent</span><span class="token3">(</span>customEvent<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token4">fireCustomEvent</span><span class="token3">(</span><span class="token2">'你好,我是普通JS!'</span><span class="token3">)</span><span class="token3">;</span> ``` ``` `content-script.js`中: ``` <pre class="calibre10">``` var hiddenDiv <span class="token1">=</span> document<span class="token3">.</span><span class="token4">getElementById</span><span class="token3">(</span><span class="token2">'myCustomEventDiv'</span><span class="token3">)</span><span class="token3">;</span> <span class="token5">if</span><span class="token3">(</span><span class="token1">!</span>hiddenDiv<span class="token3">)</span> <span class="token3">{</span> hiddenDiv <span class="token1">=</span> document<span class="token3">.</span><span class="token4">createElement</span><span class="token3">(</span><span class="token2">'div'</span><span class="token3">)</span><span class="token3">;</span> hiddenDiv<span class="token3">.</span>style<span class="token3">.</span>display <span class="token1">=</span> <span class="token2">'none'</span><span class="token3">;</span> document<span class="token3">.</span>body<span class="token3">.</span><span class="token4">appendChild</span><span class="token3">(</span>hiddenDiv<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> hiddenDiv<span class="token3">.</span><span class="token4">addEventListener</span><span class="token3">(</span><span class="token2">'myCustomEvent'</span><span class="token3">,</span> <span class="token5">function</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> var eventData <span class="token1">=</span> document<span class="token3">.</span><span class="token4">getElementById</span><span class="token3">(</span><span class="token2">'myCustomEventDiv'</span><span class="token3">)</span><span class="token3">.</span>innerText<span class="token3">;</span> console<span class="token3">.</span><span class="token4">log</span><span class="token3">(</span><span class="token2">'收到自定义事件消息:'</span> <span class="token1">+</span> eventData<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> ``` ``` ## 长连接和短连接 其实上面已经涉及到了,这里再单独说明一下。Chrome插件中有2种通信方式,一个是短连接(`chrome.tabs.sendMessage`和`chrome.runtime.sendMessage`),一个是长连接(`chrome.tabs.connect`和`chrome.runtime.connect`)。 短连接的话就是挤牙膏一样,我发送一下,你收到了再回复一下,如果对方不回复,你只能重新发,而长连接类似`WebSocket`会一直建立连接,双方可以随时互发消息。 短连接上面已经有代码示例了,这里只讲一下长连接。 popup.js: ``` <pre class="calibre10">``` <span class="token4">getCurrentTabId</span><span class="token3">(</span><span class="token3">(</span>tabId<span class="token3">)</span> <span class="token1">=</span><span class="token1">></span> <span class="token3">{</span> var port <span class="token1">=</span> chrome<span class="token3">.</span>tabs<span class="token3">.</span><span class="token4">connect</span><span class="token3">(</span>tabId<span class="token3">,</span> <span class="token3">{</span>name<span class="token3">:</span> <span class="token2">'test-connect'</span><span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> port<span class="token3">.</span><span class="token4">postMessage</span><span class="token3">(</span><span class="token3">{</span>question<span class="token3">:</span> <span class="token2">'你是谁啊?'</span><span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> port<span class="token3">.</span>onMessage<span class="token3">.</span><span class="token4">addListener</span><span class="token3">(</span><span class="token5">function</span><span class="token3">(</span>msg<span class="token3">)</span> <span class="token3">{</span> <span class="token4">alert</span><span class="token3">(</span><span class="token2">'收到消息:'</span><span class="token1">+</span>msg<span class="token3">.</span>answer<span class="token3">)</span><span class="token3">;</span> <span class="token5">if</span><span class="token3">(</span>msg<span class="token3">.</span>answer <span class="token1">&&</span> msg<span class="token3">.</span>answer<span class="token3">.</span><span class="token4">startsWith</span><span class="token3">(</span><span class="token2">'我是'</span><span class="token3">)</span><span class="token3">)</span> <span class="token3">{</span> port<span class="token3">.</span><span class="token4">postMessage</span><span class="token3">(</span><span class="token3">{</span>question<span class="token3">:</span> <span class="token2">'哦,原来是你啊!'</span><span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> ``` ``` content-script.js: ``` <pre class="calibre17">``` <span class="token">// 监听长连接</span> chrome<span class="token3">.</span>runtime<span class="token3">.</span>onConnect<span class="token3">.</span><span class="token4">addListener</span><span class="token3">(</span><span class="token5">function</span><span class="token3">(</span>port<span class="token3">)</span> <span class="token3">{</span> console<span class="token3">.</span><span class="token4">log</span><span class="token3">(</span>port<span class="token3">)</span><span class="token3">;</span> <span class="token5">if</span><span class="token3">(</span>port<span class="token3">.</span>name <span class="token1">==</span> <span class="token2">'test-connect'</span><span class="token3">)</span> <span class="token3">{</span> port<span class="token3">.</span>onMessage<span class="token3">.</span><span class="token4">addListener</span><span class="token3">(</span><span class="token5">function</span><span class="token3">(</span>msg<span class="token3">)</span> <span class="token3">{</span> console<span class="token3">.</span><span class="token4">log</span><span class="token3">(</span><span class="token2">'收到长连接消息:'</span><span class="token3">,</span> msg<span class="token3">)</span><span class="token3">;</span> <span class="token5">if</span><span class="token3">(</span>msg<span class="token3">.</span>question <span class="token1">==</span> <span class="token2">'你是谁啊?'</span><span class="token3">)</span> port<span class="token3">.</span><span class="token4">postMessage</span><span class="token3">(</span><span class="token3">{</span>answer<span class="token3">:</span> <span class="token2">'我是你爸!'</span><span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span><span class="token3">)</span><span class="token3">;</span> ``` ```