同源多页面实时通信之BroadcastChannel实现及简单封装

目录

  • 背景
  • 介绍及API使用
    • 是什么
    • 对象实例化
    • 发送消息
    • 监听消息
    • 错误处理
    • 关闭通道
  • 项目中简单封装
    • channel.js文件,引入到使用的页面中
    • 发送端send.html
    • 监听端
    • 运行测试一下吧
  • 浏览器兼容程度
  • 总结

背景

日常开发做项目时,如果采用非Vue脚手架时,肯定会碰到这样的场景:比如在浏览器中新开两个tab页面,A页面发送消息后,B页面实时监听并触发某些动作。类似的需求有很多,比如实时共享状态等等。这样的实时通信场景的解决方案我相信大家有很多想法,比如localStoragepostMessageWebSocketsharedWorker等等。
今天带来另一种方式:BroadcastChannel广播通信。如果着急,可直接跳转到“项目中简单封装章节查看封装代码”

介绍及API使用

是什么

BroadcastChannel 接口代理了一个命名频道,可以让指定 origin 下的任意 browsing context 来订阅它。它允许同源的不同浏览器窗口,Tab 页,frame 或者 iframe 下的不同文档之间相互通信。通过触发一个 message 事件,消息可以广播到所有监听了该频道的 BroadcastChannel 对象。

简单来说,就是需要在同源的情况下,实现浏览器多窗口实时进行通信,且该通信是广播进行的。

对象实例化

// 通道名称,用以区分不同的通道。对于相同的来源下的所有浏览上下文,一个名称只对应一个通道。是string类型,用来标识当前的BroadcastChannel
const channel = new BroadcastChannel(channelName)

发送消息

可以使用postMessage() 发送一条消息,给所有同源下监听了该频道的所有浏览器。消息message事件的形式发送给每一个绑定到该频道的广播频道。

 
const channel = new BroadcastChannel("test")
 
// 发送消息通知,参数是任何对象
bc.postMessage('hello xiaozong');

监听消息

发送事件后,如何使用进行消息的监听呢?当频道收到一条消息时,会在关联的BroadcastChannel对象上触发 message 事件,监听方式有两种,具体如下:

const channel = new BroadcastChannel("test")
// 消息监听 方式一
channel.onmessage = ({data}) => {
	// 这里写具体的业务逻辑
}
 
// 消息监听 方式二
channel.addEventListener('message', ({data}) => {
	// 这里写具体的业务逻辑
})

错误处理

const channel = new BroadcastChannel('test');
 
// 方式一
channel.addEventListener('messageerror', ({data}) => {
  console.error(data);
})
 
// 方式二
channel.onmessageerror = ({data}) => {
  console.log(data);
};

关闭通道

通过调用 close() 方法,可以马上断开其与对应频道的关联,并让其被垃圾回收。这是必要的步骤,因为浏览器没有其他方式知道频道不再被需要。不断开可能会导致一直处于监听状态,消耗资源,会导致不能被内存回收。

// 连接到指定频道
const channel = new BroadcastChannel('test');

// 当完成后,断开与频道的连接
channel.close();

项目中简单封装

channel.js文件,引入到使用的页面中

/**
 * 简单封装BroadcastChannel的用法
 */
const Channel = {
    /**
     * BroadcastChannel对象
     */
    channel: null,

    /**
     * 实例化BroadcastChannel对象,赋值给channel变量
     * @param {*} channelName 通道名称,用以区分不同的通道
     * @returns 
     */
    getChannel: (channelName) => {
        Channel.channel = new BroadcastChannel(channelName)
        return Channel.channel
    },

    /**
     * 发送消息
     * @param {*} object 消息体
     */
    send: (object) => {
        Channel.channel.postMessage(object)
    },

    /**
     * 发送消息,重载方法,可直接调用,省略对象实例化操作
     * @param {*} channelName 通道名称,用以区分不同的通道
     * @param {*} object 消息体
     */
    send: (channelName, object) => {
        if (Channel.channel == null) {
            Channel.channel = Channel.getChannel(channelName)
        }
        Channel.channel.postMessage(object)
    },

    /**
     * 监听消息
     * @param {*} callback 回调函数
     */
    listen: (callback) => {
        Channel.channel.onmessage = ({ data }) => {
            callback(data)
        }
    },

    /**
     * 监听消息,重载方法,可直接调用,省略对象实例化操作
     * @param {*} channelName 通道名称,用以区分不同的通道
     * @param {*} callback 回调函数
     */
    listen: (channelName, callback) => {
        if (Channel.channel == null) {
            Channel.channel = Channel.getChannel(channelName)
        }
        Channel.channel.onmessage = ({ data }) => {
            callback(data)
        }
    },

    /**
     * 通道关闭
     */
    close: () => {
        Channel.channel.close()
    },

    /**
     * 通道关闭,重载方法,可直接调用,省略对象实例化操作
     * @param {*} channelName 通道名称,用以区分不同的通道
     */
    close: (channelName) => {
        if (Channel.channel == null) {
            Channel.channel = Channel.getChannel(channelName)
        }
        Channel.channel.close()
    },

    /**
     * 通道枚举,定义业务中需要用到的所有通道名称枚举,可根据业务需求无限扩容
     */
    channelEnum: {
        TEST: { name: 'test', coment: '测试通道' },
        REAL_EVENT: { name: 'real_event', coment: '实时事项通道' },
    }
}

发送端send.html

使用方式测试如下:

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>标签页通信-发送端title>
    <script src="./channel.js">script>
head>

<body>
    标签页通信-发送端
body>
<script>
    Channel.send(Channel.channelEnum.TEST.name, 'hello xiaozong')

script>

html>

监听端

使用方式测试如下:

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>标签页通信-接收端title>
    <script src="./channel.js">script>
head>

<body>
    标签页通信-接收端
body>
<script>
    Channel.listen(Channel.channelEnum.TEST.name, (data) => {
        document.write(`

${data}

`
) })
script> html>

运行测试一下吧

就只贴一下图吧 ,比较简单
同源多页面实时通信之BroadcastChannel实现及简单封装_第1张图片
同源多页面实时通信之BroadcastChannel实现及简单封装_第2张图片

浏览器兼容程度

同源多页面实时通信之BroadcastChannel实现及简单封装_第3张图片

总结

昨晚十二点左右心血来潮,太晚了也只进行了简单的封装,基本满足常规场景的使用,如果实际项目开发中有用到的话,可以优化后使用。

你可能感兴趣的:(开发百宝箱系列,前端,javascript,vue.js)