Cross-Context Communication in BroadcastChannel API
The broadcast channel API allows basically communication between browsing contexts(that is, tabs, windows, frames or iframes) and workers on the same origin.
You don't have to maintain the reference to the frames or workers you wish to communicate with, just constructing your own BroadcastChannel
instance with the channel name which is capable of bidirectional communication between all of them.
It's self-contained interface and allows cross-context communication, what a perfect solution to detect user actions in other tabs within the same origin, like when user logs in or out, to transmit data across them and keep the states in sync. That's of great benefit to multiple tabs apps.
Scenario: log in status
It's a way common feature to show the log in status after user pass authentication and walk around, and open the other tabs to log out. But he or she is still in logged in status on the first tab, which will make user confused, until anything he or she touch will either redirect to the login page or straight blow up in their face. A more inviting alternative is to figure out that they've logged out and do something about it, like, display a hint to asking them to log in again.
let loginStatus = 'login', originalLoginStatus = 'login'
window.addEventListener('focus', () => {
if (loginStatus !== originalLoginStatus) {
alert('You have logged out on other tab already!')
location.reload()
}
})
const bc = new BroadcastChannel('login-status')
bc.onmessage = evt => {
loginStatus = evt.data
}
document.querySelector('#logout').addEventListener('click', () => {
bc.postMessage('logout')
})
Creating or Joining a Channel
To their credit, the form of creating a channel is identical to that of joining, it means the new channel would be created automatically for us if it isn't existing yet.
So there is no more simple to do like below.
const channelName = 'update'
const bc = new BroadcastChannel(channelName)
Sending Messages
It's enough to call the postMessage
method on the created BroadcastChannel
instance with an argument for any types.
Note that this API doesn't associate any sematics(messaging protocol) to messages (there is no negotiation nor requirement from specifcation), it's all up to you to know what kind of messages to expect and what to do with them.
And what exactly types could we pass in? The answer is all types that can be cloned using the structured clone algorithm, including the followings:
- All primitive types except symbol(
boolean
,string
,number
,null
,undefined
) - Dates
- Regular Expressions
- Blobs
- Files and FileLists
- ArrayBuffers and ArrayBufferViews
- ImageBitmaps, ImageDatas
- Boolean, Number, Arrays, Objects, Maps and Sets
br.postMessage('hi')
Receiving Messages
Once a message is posted, a message
event will be dispatched to the BroadcastChannel
instance connected to this channel. And the event handler binding by onmessage
method on the created BroadcastChannel
object will be run.
br.onmessage = evt => {
const message = evt.data
// processing
}
Disconnecting a Channel
It might make sense to call close
method on the BroadcastChannel
object when we do not use it anymore, this would disconnect the object from the underlying channel, and once there is no references to that channel any longer, allowing garbage collection.
br.close()
Compatiblity
This API has landed in Chrome years ago, but if you have to make your app work on the legacy browser, to my knowledge, the most popular one is here(https://github.com/pubkey/broadcast-channel). You can use it almost exactly like the native API. It will switch up to the native API automatically if supporting for the more efficient result, otherwise, it will utilize IndexedDB or LocalStorage. We can register storage
event on window object to detect any changes(additions, updates and deletions) of value on LocalStorage.
window.addEventListener('storage', (evt) => {
if (evt.key == 'myname') {
console.log(evt.newValue)
}
})
localStorage.setItem('myname', 'hi')
欢迎添加我的公众号一起深入探讨技术手艺人的那些事!
如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!