概述
web通信主要是为了实现独立上下文之间共享数据, 满足业务需求, 同时尽量避免暴露数据的一种技术手段。通常可通过获取句柄、共享存储或结合事件通知等方式来实现。
html文档间通信
实现方式主要有
- 父子文档通信(借助window对象postMessage);
- 借助localStorage监听;
- 使用广播隧道(BroadcastChannel);
- 借助服务器向另一窗口推送信息(WebSocket, SSE)。
父子文档通信
window对象上的postMessage方法包含三个参数:
- message为消息体,它将会被结构化克隆算法序列化
- targetOrigin定义窗口的origin属性, 保证协议、主机地址或端口的完全匹配
- transfer是一个消息上下文, 供目标环境调用
parent.html:
const childPage = window.open('./child.html', 'child')
childPage.onload = () => {
childPage.postMessage('hello', location.origin);
}
child.html:
window.onmessage = evt => {
console.log(evt)
// evt.data
}
iframe通信与此类似, 获取对象分别为window.frames[0]和window.parent。 此方案比较普遍, 局限在html为父子关系的页面。
localStorage监听
源页面
localStorage.setItem('message', 'hello')
接收页面
window.onstorage = e => {
console.log(e.key, e.oldValue, e.newValue)
}
实现较简单, 但数据会暴露给Web Storage, 并且赋值两次相同时onStorage不会触发。
广播隧道BroadcastChannel
发送页面
const channel = new BroadcastChannel('tab')
channel.postMessage('hello')
接收页面
const channel = new BroadcastChannel('tab')
channel.onmessage = e => {
console.log(e)
}
数据较安全, 但是兼容性不好。
服务端推送
WebSocket为全双工通道, 可以双向通信; SSE为单向通道, 只能服务器向浏览器发送。实现方法可以参考Server-Sent Events 教程。
Web Worker间通信
worker的postMessage方法向worker内部作用域发送一个消息, worker本身可以使用DedicatedWorkerGlobalScope.postMessage 方法将信息发送回生成它的线程。worker没有浏览器环境, postMessage方法可有两个参数, 第一个为消息体, 第二个是隧道数组。
调用worker的文档
var w1 = new Worker('worker1.js')
var w2 = new Worker('worker2.js')
var mc = new MessageChannel()
w1.postMessage('port1', [mc.port1])
w2.postMessage('port2', [mc.port2])
w2.onmessage = function(e) {
console.log(`get ${e.data} on html`)
}
worker1.js
self.onmessage = e => {
console.log(`get ${e.data} from ${e.currentTarget.origin}`)
const port = e.ports[0]
port.postMessage('message send from worker1')
port.onmessage = function(evt) {
console.log(`get ${evt.data} on ${e.currentTarget.location}`)
}
}
worker2.js
self.onmessage = e => {
console.log(`get ${e.data} from ${e.currentTarget.origin}`)
const port = e.ports[0]
port.onmessage = function(evt) {
console.log(`get ${evt.data} on ${e.currentTarget.location}`)
postMessage(`${evt.data} from worker2`)
}
}
可以看到控制台输出
get port1 from http://localhost:端口号
get port2 from http://localhost:端口号
get message send from worker1 on http://localhost:端口号/worker2.js
get message send from worker1 from worker2 on html
还有一种允许多个脚本共享单一worker的Shared Web Workers, 兼容性不太好, 以后有机会再说。