We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
刚刚无意之中在网上看到 web worker 这个词,瞬间跌入了回忆的深渊,什么 serviceWorker、walking dead、亲爱的长者 乱七八糟一齐涌了出来,就是没有 web worker,最后颓废的发现,原来关于 worker 相关的东西自己已经忘干净了。
web worker
serviceWorker
walking dead
worker
不过隐约记得这东西用起来不很复杂,于是果断上 MDN 看看文档,但是又无意中看到 MessageChannel 这个词,很明显幼小的心灵又被一个陌生的词汇狠狠的鞭笞了一下。忍不住点进去看了一下关于 MessageChannel 的资料,发现这行代码 (来源):
MessageChannel
otherWindow.postMessage('Hello from the main page!', '*', [channel.port2]);
蛤?原来 postMessage 方法可以接收第 3 个参数?那请问第三个参数是什么意思?文章里说的 MessgaePort 对象又是什么?想到这么多问题自己还完全摸不着边,而且问题之间环环相扣,于是仔细深究了一番,下面是一些笔记。
postMessage
3
MessgaePort
首先,MessageChannel 是一个构造函数,创建一对相互连接的 MessagePort 对象。
MessagePort
var mc=new MessageChannel; mc.port1.onmessage=function(e){console.log("port1:"+e.data);}; mc.port2.onmessage=function(e){console.log("port2:"+e.data);}; mc.port1.postMessage("1"); //会在port2的message事件收到 mc.port2.postMessage("2"); //会在port1的message事件收到
(这段代码的来源)
上面说的一对相互连接的 MessagePort 对象分别是 mc.port1 和 mc.port2,他们可以相互给对方发送消息,并且处理接收到的消息。这就是 MessageChannel 最基本的用法,很简单,但是看起来很无用。
mc.port1
mc.port2
我们知道 postMessage 可以用于 worker 和跨文档消息传递机制,用起来也很简单,只要记住 postMessage 是”自己给自己发消息“,我们以后者为例:
// index.html var iframe = document.querySelector('iframe') iframe.onload = () => { let w = iframe.contentWindow w.onmessage = (e) => { console.log(e.data) } w.postMessage('initialize', '*') } // iframe.html onmessage = (e) => { console.log(e.data) window.postMessage(`Received message: ${e.data}`) }
上面这段代码逻辑非常简单,iframe 自己给自己发消息并处理接收到的消息,但是可能不好理解的就是所谓的 “自己给自己发消息”。在我们正常的认知中,发消息通常都是一方发给另外一方,哪有自己给自己发消息的 (自言自语除外)。而且因为自发消息的特性,在不同的上下文中 (不同的页面或者 worker 与 main thread 间),postMessage 和 onmessage 在写法上看起来又不像是 ”自己给自己发“:在 index.html 中写法是 iframe.contentWindow.postMessage,在 iframe.html 中写法又是 window.postMessage。总之,如果不习惯的话从文字上看起来会觉得这种写法有些别扭,不过好在我们还有另一种写法。
iframe
onmessage
index.html
iframe.contentWindow.postMessage
iframe.html
window.postMessage
上面说到 MessageChannel 构造函数会创建一对相互关联的 MessagePort 对象,怎么理解 MessagePort 对象呢?可以把一个 MessagePort 对象当成消息传递的管道,我们可以通过这两个对象来进行消息的传递,想象一下现实生活中的两个人通过手机打电话,一个 MessagePort 对象就是一个手机。
MessageChannel 其实就更好理解了,不再是自己给自己发消息,而是一方发给另一方,也即是 mc.port1 和 mc.port2 相互传递处理消息。
但是怎么利用 MessageChannel 来让我们现有的消息传递机制更加简单清晰呢?答案正是我们上面说的 window.postMessage 方法的第三个参数 (MessagePort.postMessage 里是第二个参数)。
MessagePort.postMessage
这个参数是可选的,而且如果有的话必须是 Array/ArrayLike,表示 “跟随信息一起传送的可转让对象的序列”,意思就是把这些可转让对象的所有权转让给目的上下文 (注意只有 MessagePort 和 ArrayBuffer 对象可以被转移)。
Array/ArrayLike
ArrayBuffer
什么意思呢?看文章里的第一行代码:
这里 channel.port2 就被转移到 otherWindow 上下文中去了,也就是现在 channel.port2 能够被 otherWindow 上下文访问到了。不过这样有什么用呢?我们不还是用了 postMessage 方法吗?
channel.port2
otherWindow
其实很好理解,想像一下,现在你跟你的一个小伙伴分隔两地,通信是通过邮政寄信的方式,很不方便,但是某天你突然得到两个电话,然后通过邮政发给你的小伙伴一个,这样等小伙伴收到电话你们就能愉快的打电话了。翻译过来就是:通过 postMessage 把其中一个 MessagePort 对象传递给需要通信的一方,然后双方就可以通过 MessagePort 对象相互通信。
好了就说这么多,下面直接上完整的代码,很好理解:
// index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title></title> </head> <body> <iframe src="iframe.html" frameborder="0"></iframe> <script> var channel = new MessageChannel var port = channel.port1 var iframe = document.querySelector('iframe') iframe.onload = () => { iframe.contentWindow.postMessage('initialize', '*', [channel.port2]) } port.addEventListener('message', (e) => { console.log(e.data, e) }, false) // 因为我们用的是 addEventListener 而不是 onmessage, // 所以需要调用 port.start 方法 // 见: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort port.start() </script> </body> </html> // iframe.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title></title> </head> <body> <script> onmessage = (e) => { // index.html 传递过来的 MessagePort 对象 // 通过 e.ports[0] 获取 var port = e.ports[0] // 现在可以愉快的通过 port // 进行消息传递 port.onmessage = (e) => { console.log('from iframe: ' + e.data) } port.postMessage('sad') } </script> </body> </html>
MessageChannel 是基本的双向信息传输管道,可以把它想象成 window.postMessage/window.onmessage 的另一种更加清晰的替代方案。最后要说的一点就是,sharedWorker 默认使用了 MessagePort 进行消息传递,有兴趣的同学可以自行探索。
window.onmessage
参考链接:
MDN 7 Things You Need To Know About Web Workers Messing with MessageChannel
The text was updated successfully, but these errors were encountered:
No branches or pull requests
刚刚无意之中在网上看到
web worker
这个词,瞬间跌入了回忆的深渊,什么serviceWorker
、walking dead
、亲爱的长者 乱七八糟一齐涌了出来,就是没有web worker
,最后颓废的发现,原来关于worker
相关的东西自己已经忘干净了。不过隐约记得这东西用起来不很复杂,于是果断上 MDN 看看文档,但是又无意中看到
MessageChannel
这个词,很明显幼小的心灵又被一个陌生的词汇狠狠的鞭笞了一下。忍不住点进去看了一下关于MessageChannel
的资料,发现这行代码 (来源):蛤?原来
postMessage
方法可以接收第3
个参数?那请问第三个参数是什么意思?文章里说的MessgaePort
对象又是什么?想到这么多问题自己还完全摸不着边,而且问题之间环环相扣,于是仔细深究了一番,下面是一些笔记。什么是 MessageChannel ?
首先,
MessageChannel
是一个构造函数,创建一对相互连接的MessagePort
对象。(这段代码的来源)
上面说的一对相互连接的
MessagePort
对象分别是mc.port1
和mc.port2
,他们可以相互给对方发送消息,并且处理接收到的消息。这就是MessageChannel
最基本的用法,很简单,但是看起来很无用。postMessage 很好用
我们知道
postMessage
可以用于worker
和跨文档消息传递机制,用起来也很简单,只要记住postMessage
是”自己给自己发消息“,我们以后者为例:上面这段代码逻辑非常简单,
iframe
自己给自己发消息并处理接收到的消息,但是可能不好理解的就是所谓的 “自己给自己发消息”。在我们正常的认知中,发消息通常都是一方发给另外一方,哪有自己给自己发消息的 (自言自语除外)。而且因为自发消息的特性,在不同的上下文中 (不同的页面或者 worker 与 main thread 间),postMessage
和onmessage
在写法上看起来又不像是 ”自己给自己发“:在index.html
中写法是iframe.contentWindow.postMessage
,在iframe.html
中写法又是window.postMessage
。总之,如果不习惯的话从文字上看起来会觉得这种写法有些别扭,不过好在我们还有另一种写法。MessageChannel 让通信变得更简单
上面说到
MessageChannel
构造函数会创建一对相互关联的MessagePort
对象,怎么理解MessagePort
对象呢?可以把一个MessagePort
对象当成消息传递的管道,我们可以通过这两个对象来进行消息的传递,想象一下现实生活中的两个人通过手机打电话,一个MessagePort
对象就是一个手机。MessageChannel
其实就更好理解了,不再是自己给自己发消息,而是一方发给另一方,也即是mc.port1
和mc.port2
相互传递处理消息。但是怎么利用
MessageChannel
来让我们现有的消息传递机制更加简单清晰呢?答案正是我们上面说的window.postMessage
方法的第三个参数 (MessagePort.postMessage
里是第二个参数)。这个参数是可选的,而且如果有的话必须是
Array/ArrayLike
,表示 “跟随信息一起传送的可转让对象的序列”,意思就是把这些可转让对象的所有权转让给目的上下文 (注意只有MessagePort
和ArrayBuffer
对象可以被转移)。什么意思呢?看文章里的第一行代码:
这里
channel.port2
就被转移到otherWindow
上下文中去了,也就是现在channel.port2
能够被otherWindow
上下文访问到了。不过这样有什么用呢?我们不还是用了postMessage
方法吗?其实很好理解,想像一下,现在你跟你的一个小伙伴分隔两地,通信是通过邮政寄信的方式,很不方便,但是某天你突然得到两个电话,然后通过邮政发给你的小伙伴一个,这样等小伙伴收到电话你们就能愉快的打电话了。翻译过来就是:通过
postMessage
把其中一个MessagePort
对象传递给需要通信的一方,然后双方就可以通过MessagePort
对象相互通信。好了就说这么多,下面直接上完整的代码,很好理解:
总结
MessageChannel
是基本的双向信息传输管道,可以把它想象成window.postMessage
/window.onmessage
的另一种更加清晰的替代方案。最后要说的一点就是,sharedWorker 默认使用了MessagePort
进行消息传递,有兴趣的同学可以自行探索。参考链接:
MDN
7 Things You Need To Know About Web Workers
Messing with MessageChannel
The text was updated successfully, but these errors were encountered: