MessageChannel和BroadcastChannel

MessageChannel

是一种点对点的通讯方式,能够理解为管道,消息从一端进入另外一端输出,最基本的使用方式以下:vue

const mc = new MessageChannel()

mc.port1.onmessage = function(eve){
    console.log(eve.data)
}
mc.port2.postMessage('hi jack')

// 'hi jack'
复制代码

基本使用方式了解了,那使用场景是什么呢?框架

最多见的就是iframes之间通讯了,好比我有一个页面,里面嵌入了iframeA和iframeB,两个iframe之间通讯咱们能够经过top window来传送port,而后两个子页面就能够直接对话了。dom

// frameA
const mc = new MessageChannel()
window.parent.postMessage(1, 'http://localhost:8080', [mc.port1])

// top window
window.addEventListener('message', (eve) => {
    window.frames[1].postMessage(1, 'http://localhost:8081', [eve.ports[0]])
})

// frameB
var port

window.addEventListener('message', (eve) => {
    port = eve.ports[0]
})

btn.onclick = () => {
    port.postMessage({message: 'hi b, i am a'})
}
复制代码

MessageChannel最厉害地方在于竟然能够被postMessage传输,由于它实现了transferable接口。同理top window再将这个port下方到iframeB,那么他们两个子页面就能够直接通讯了。异步

deepClone

除了用做通讯,MessageChannel还有一些hack的用法,好比有人用它来作deepClone函数

function deepClone(target) {
    return new Promise(resolve => {
        const channel = new MessageChannel()
        channel.port2.postMessage(target)
        channel.port1.onmessage = eve => {
            resolve(eve.data)
        }
    })
}

const obj = {
    name: '123',
    b: {
        c: 456
    },
    d: undefined
}

deepClone(obj).then(d => console.log(d))
复制代码

这个方法比较优秀的地方在于undefined的不会丢失,循环引用的对象也不会报错,循环点会被置为undefined,不过不能复制函数。oop

不知大家有没有注意到一个小细节,上面的代码里面咱们是先执行了postMessage,在去添加onmessage监听,为何还能接受到消息呢?咱们再激进一点post

const mc = new MessageChannel()
const { port1, port2 } = mc

port1.postMessage(123)
setTimeout(() => {
    port2.onmessage = (eve) => {
        console.log('received message: ', eve.data);
    }
}, 1000);

// 123
复制代码

数据依然能够被显示。我没有去查证,推测这里应该有一个缓冲区的概念,当数据被post之后,先存在缓冲区,当onmessage监听器一旦绑定就消费这些数据。ui

event loop

还有一个黑科技的用法就是,鉴于postMessage数据而后onmessage消费数据,这是一个异步任务,vue等框架用它来模拟nextTick的行为spa

// 用MessageChannel去作setImmediate的polyfill
// 原理是将新的message事件加入到原有的dom events以后
if (typeof MessageChannel === 'function') {
    var channel = new MessageChannel();
    var port = channel.port2;
    channel.port1.onmessage = nextHandler;
    port.postMessage(1);
}
复制代码

除了MessageChannel,其余还有setTimeout、setImmediate、MutationObsever、Promise.then等等能够实现相似效果。code

BroastcastChannel

从名字能够看出来,这是一个广播形式的通道,因此就不是点对点了,而是相似于发布订阅的一种形式,只要有人订阅了个人消息,就能够接收到消息

const bc = new BroadcastChannel('channel1')
const bc2 = new BroadcastChannel('channel1')

bc2.onmessage = (eve) => {
    console.log('bc:', eve.data);
}

setTimeout(() => {
    bc.postMessage({message: 'hello'})
}, 1000);

// {message: 'hello'}
复制代码

由于是广播,天然也不存在缓冲区的概念了,你错过了就没了,因此上面那个例子,先postMessage再绑定onmessage就接收不到消息了。

BroadcaChannel用在多个子页面监听父页面的状况下仍是很好用的。

相关文章
相关标签/搜索