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用在多個子頁面監聽父頁面的狀況下仍是很好用的。

相關文章
相關標籤/搜索