更多场景下的信道创建
广播频道(BroadcastChannel)
在 快速开始 章节,介绍了两个同源窗口可以创建广播频道 作为信道,实现信道模块的相互调用。
var channel = new BroadcastChannel();接下来,我将继续列举一些常见的例子,展示如何在更多场景中获取或创建信道,以支持信道模块的通信。
工作线程(Worker)
在主线程内,可以使用 Worker 实例作为与工作线程通信的信道。
var channel = new Worker('worker.js');在 Worker 线程内,可以使用 self 属性作为与主线程通信的信道。
var channel = self;内嵌窗口(Iframe)
在父窗口内,可以通过 HTMLIFrameElement.contentWindow 属性获取内嵌窗口的引用,并创建信道。
var iframe = document.querySelector('iframe');
var channel = {
postMessage(data) {
iframe.contentWindow?.postMessage?.(data, '*');
},
};
self.addEventListener('message', function(ev) {
if (ev.source === iframe.contentWindow) {
channel?.onmessage?.(ev);
}
});在内嵌窗口里,可以通过 parent 属性获取父窗口的引用,从而创建信道。
var channel = {
postMessage(data) {
parent.postMessage(data, '*');
},
};
self.addEventListener('message', function(ev) {
channel?.onmessage?.(ev);
});后台脚本(Background scripts)
在内容脚本里,可以使用 chrome.runtime.connect 方法连接后台脚本,并从返回的 port 对象创建出信道。
var port = chrome.runtime.connect();
var channel = {
postMessage(data) {
port.postMessage(data);
},
};
port.onMessage.addListener(function(data, port) {
channel?.onmessage?.(data);
});在后台脚本内,可以通过 chrome.runtime.onConnect 事件监听器接收内容脚本的连接请求,同样从 port 对象创建出信道。
chrome.runtime.onConnect.addListener(function(port) {
var channel = {
postMessage(data) {
port.postMessage(data);
},
};
port.onMessage.addListener(function(data, port) {
channel?.onmessage?.(data);
});
});提示
如果你比较细心,可能会留意到原生的 onmessage 的回调参数有的是包含了 data 属性的 MessageEvent 对象,而有的则是普通的消息值。
信道模块内部做了兼容,同时支持这两类数据结构。
进程间通信(IPC)
在父进程内,可以通过 spawn 方法创建子进程,并将 stdio 数组的第 4 个参数指定为 'ipc'。
var childProcess = spawn('node', ['child.js'], {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
});然后,从子进程的引用对象创建出信道。
var channel = {
postMessage(data) {
childProcess.send(data);
},
};
childProcess.on('message', function(data) {
channel?.onmessage?.(data);
});在子进程内,可以从 process 对象创建出信道。
var channel = {
postMessage(data) {
process.send(data);
},
};
process.on('message', function(data) {
channel?.onmessage?.(data);
});网络套接字(WebSocket)
当两个 JS 运行环境需要通过网络进行通信时,可以使用 WebSocket 作为信道。
信道模块底层发送的消息包含两类数据: 一类是调用栈中由调用者决定的参数,另一类是信道模块自身维护的其他属性。后者能保证可以被 JSON.stringify 方法序列化,也能被结构化克隆算法处理。
由于 WebSocket 未提供支持结构化克隆算法的 postMessage 方法,你可能需要自行设计序列化和反序列化算法,并确保序列化后的数据能够作为 WebSocket 实例上 send 方法的参数。
不过,在大多数情况下,如果你能保证调用栈包含的参数可以被 JSON.stringify 方法序列化为字符串,那么可以参考下面的示例代码,从 WebSocket 实例创建出信道。
var channel = {
postMessage(data) {
webSocket.send(JSON.stringify(data));
},
};
webSocket.onmessage = function(ev) {
channel?.onmessage?.({
data: JSON.parse(ev.data),
});
};