入门系列3 - background、content、popup的通信

·  阅读 12432

前言

😋😋😋嘿,各位爷好~~

前面两节简单的总结了一些关于什么是浏览器插件,以及浏览得的manifest.json文件配置。同时在后续也给插件增加了页面和逻辑,说明了backgroundpopupcontent三个字段的具体使用。

不同字段对应着不同的作用,协作构成了插件的强大功能。合作就得沟通,就像Electron的主进程和渲染进程通信,那它们之间是如何通信的呢?

插件的架构体系

插件呢,必须具有存在位于浏览器工具栏中的图标,工具栏图标允许轻松访问,并使用户了解安装了哪些插件。大多数用户通过单击图标,使用其弹出窗口进行交互,比如CORS跨域插件,谷歌翻译插件等等。

插件的体系结构是取决于其功能,但大多数功能强大的插件包括以下多个组件:

  • manifest
  • background scripts
  • ui elements ==> popup
  • content scripts
  • optional page

备注:就像插件允许用户自定义Chrome浏览器一样,optional page可以自定义插件。在chrome40以前,使用options_page配置;在chrome40以后,则使用options_ui配置。

组件的backgroundpopupcontent三个字段整体关系图如下:

通信

插件的不同组件之间通常需要彼此通信,不同的HTML页面可以使用chrome.extension方法找到彼此,例如getViews()和getBackgroundPage()。一旦页面引用了其他扩展页面,第一个页面就可以调用其他页面上的函数并操纵它们的DOM。此外,插件的所有组件还可以使用storage API存储值,并使用消息传递进行通信。

而对于组件backgroundpopupcontent,具体通信可以看下图:

可以看到存在6种通信路径:

  1. popupbackground之间的通信
    1. backgroundpopup发送消息
    2. popupbackground发送消息
  2. backgroundcontent之间的通信
    1. backgroundcontent发送消息
    2. contentbackground发送消息
  3. popupcontent之间的通信
    1. popupcontent发送消息
    2. contentpopup发送消息

脚本权限

我们已经了解了存在的几种通信路径,通信意味着各种API的相互调用,因此在实践之前,也需要去了解一点关于chrome 插件的脚本权限

脚本的类型决定着脚本存在什么权限:比如Chrome APIDOM 访问跨域访问原页面JS访问,具体如图:

=====> 图使用markdown编辑 好了,了解了权限以后,就知道什么脚本可以使用何种API来实现信息传递,接下来就尝试去打通每一关吧。

popupbackground之间的通信

首先,给一个大致通信图。关于content scriptpopup scriptbackground script,它们之间的通信总体概览图如下:

开始吧。还是和以前一样,新建插件文件夹,增加必须的manifest.json和基本文件。

backgroundpopup发送消息

插件的background,对于浏览器只存在一个,而对于popup,不同的 tab 就会存在一个前端,如果background需要给不同前端发送信息,就需要特殊的tab id。这里是针对backgroundpopup传递信息。

background.js 添加代码:

function toPopup() {
    alert('to popup!')
}
复制代码

popup.js 添加代码:

const bg = chrome.extension.getBackgroundPage()
document.getElementById('rBgInfo').onclick = function() {
    bg.toPopup()
}
复制代码

popup.html引入popup.js,并添加id为rBgInfo的按钮,安装插件,点击按钮,如果弹窗如下样式,则表明成功。

popupbackground发送消息

background => popup 是通过getBackgroundPage,而popup => background是通过getViews

下面就来瞧一下

使用长连接

popup.js增加如下代码:

// 使用长连接
let port = chrome.extension.connect({
    name: 'popup-name'
})

// 使用postMs 发送信息
port.postMessage('给 background 传递信息~')

// 接收信息
port.onMessage.addListener(msg => {
    console.log('接收的信息:', msg)
})
复制代码

background.js 增加如下代码:

// 获取所有 tab
const pups = chrome.extension.getViews({
    type: 'popup'
}) || []

// 输出第一个使用插件页面的url
if (pups.length) {
    console.log(pups[0].location.href)
}
复制代码

点击插件刷新按钮,点击【背景页】按钮,可以看到每次点击一下插件图标,就会发送一次信息。

这也告诉了 chrome 插件的另一个机制:点击图标出现和隐藏popup弹窗页面,实际上是对整个页面的销毁,类似于关闭网页,而不是切换网页。(很重要的哦)

操作 DOM

除了信息传递,background可能也需要对popup.html的页面进行操作,比如检测到当前是万圣节🎃,给插件页面添加个happy halloween

首先给popup.html增加一个text

<p id="pbText">不是万圣节</p>
复制代码

然后只需要在background.js中如下处理:

// 使用长连接 - 监听 popup 传递来的消息
chrome.extension.onConnect.addListener(port => {
    console.log('连接中------------')
    port.onMessage.addListener(msg => {
        console.log('接收消息:', msg)
        getAll()
        port.postMessage('popup,我收到了你的信息~')
    })
})

// 获取所有 tab
function getAll() {
    const views = chrome.extension.getViews({
        type: 'popup'
    })

    for (let o of views) {
        console.log(111)
        o.document.getElementById('pbText').innerHTML = "万圣节🎃快乐"
    }
}
复制代码

添加getAll()函数,将函数防止长连接即可。这里主要想展示chrome.extension.getViews函数的使用。

刷新插件,点击插件图标,就会弹窗如下页面了:

popupcontent之间的通信

有了backgroundpopup,下面需要做的就是创建一个content页面。

manifest添加下列配置

{
    ...
    "content_scripts": [
        {
            "matches": [
                "<all_urls>"
            ],
            "js": [
                "content.js"
            ]
        }
    ]
}
复制代码

contentpopup发送消息

首先在content.js添加如下代码:

// Chrome提供的大部分API是不支持在content_scripts中运行
// sendMessage onMessage 是可以使用
chrome.runtime.sendMessage({
    info: "我是 content.js"
}, res => {
    // 答复
    alert(res)
})
复制代码

代码负责发送信息和接收反馈,然后给popup.js添加:

chrome.runtime.onMessage.addListener((req,sender, sendResponse) => {
    sendResponse('我收到了你的来信')
    console.log('接收了来自 content.js的消息', req.info)
})
复制代码

代码负责接收消息和发送反馈。

刷新插件,点击插件按钮,打开一个页面,保持插件popup处于活跃状态(上面讲了哈~,插件关闭等于页面销毁),然后刷新页面,会发现浏览器弹出弹窗:

最后,右键插件图标,点击“审查弹窗内容”,可以看到content.jspopup.jsconsole.log日志(👻这等于告诉您如何调试插件~)

弹窗说明我们的程序是成功运行的,日志打印表明我们的通信是成功的,现在我们已经知道了contentpopup发送消息。

popupcontent发送消息

其实上面已经告诉了popupcontent发送信息了,但毕竟不是popup主动地,谈恋爱了,肯定需要主动一些了。

popup添加如下代码,放入rBgInfo按钮点击事件:

// popup ---> content
chrome.tabs.query({
    active: true,
    currentWindow: true
}, (tabs) => {
    let message = {
        info: '来自popup的情书💌'
    }
    chrome.tabs.sendMessage(tabs[0].id, message, res => {
        console.log('popup=>content')
        console.log(res)
    })
})
复制代码

寄送一封信,content得接收信:

// get popup2content info
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    console.log(request.info)
    sendResponse('我收到了你的情书,popup~')
})
复制代码

点击插件刷新按钮,打开页面,点击弹窗的rBgInfo按钮,日志打印如下:

关于popupcontent的通信又又又成功了~

backgroundcontent之间的通信

backgroundcontent之间的通信与popupcontent类似的,写者就不写demo了,与上面一样。

长连接与短连接

在上面的一些demo中,可以看到通信使用了两个函数,一个就是sendMessage,另一个就是connect,其实这两个分别对应着不同的连接方式:

  • 长连接: chrome.tabs.connectchrome.runtime.connect
  • 短连接: chrome.tabs.sendMessage

总结

了解了脚本之前的通信以后,才算真正的入门了,希望各位能学到点什么。下面关于chrome 插件的博客,可能会涉及到具体真正插件开发的实践了,更新或许会慢一点,见谅❤❤❤!

备注:这几天工作较忙,写博客都得挤时间了。希望大家也能在工作之余过好每天,谢谢时光~

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改