postMessage() 方法参数详解
postMessage()方法有两种形式:
1. Window.postMessage()
javascript
otherWindow.postMessage(message, targetOrigin, [transfer]);2. Worker.postMessage() 和 MessagePort.postMessage()
javascript
worker.postMessage(message, [transfer]); // 或 port.postMessage(message, [transfer]);参数说明
第一个参数:message(必需)
- 要发送的数据
- 可以是任何可序列化的 JavaScript 对象
- 浏览器会使用结构化克隆算法来复制数据
- 可以包含循环引用
第二个参数:targetOrigin(仅 Window.postMessage 需要)
- 指定目标窗口的源(协议+主机+端口号)
- 用于安全检查,防止消息发送到错误的域
- 可以设置为 "*" 表示不限制目标源(不推荐)
targetOrigin
指定哪些窗口能接收消息
可以是具体的域名(如 "https://example.com")或通配符:
"*":发送到任何窗口(不安全)
"/":发送到同源窗口
第三个参数:transfer(可选)
- 一个数组,包含需要转移所有权的对象
- 主要用于 Transferable 对象,如 ArrayBuffer、MessagePort 等
- 转移后,发送方将失去对这些对象的访问权限
可转移对象类型
// 1. ArrayBuffer const buffer = new ArrayBuffer(1024); window.postMessage(buffer, '*', [buffer]); // buffer 现在在发送方变为不可用 // 2. MessagePort const channel = new MessageChannel(); window.postMessage('message', '*', [channel.port2]); // 3. ImageBitmap const imageBitmap = await createImageBitmap(image); window.postMessage({ image: imageBitmap }, '*', [imageBitmap]); // 4. OffscreenCanvas const offscreen = new OffscreenCanvas(256, 256); window.postMessage({ canvas: offscreen }, '*', [offscreen]);
可转移对象 vs 结构化克隆
结构化克隆(默认行为)
const worker = new Worker('./worker.js'); const buffer = new ArrayBuffer(8); console.log('发送前 buffer 大小:', buffer.byteLength); // 8 // 结构化克隆 - 数据被复制 worker.postMessage(buffer); console.log('发送后 buffer 大小:', buffer.byteLength); // 8 (仍然可以访问)可转移对象
const worker = new Worker('./worker.js'); const buffer = new ArrayBuffer(8); console.log('发送前 buffer 大小:', buffer.byteLength); // 8 // 转移对象 - 所有权转移给 worker worker.postMessage(buffer, [buffer]); console.log('发送后 buffer 大小:', buffer.byteLength); // 0 (失去访问权限)使用示例
基本使用
javascript
// 父窗口发送消息 const iframe = document.querySelector('iframe'); const buffer = new ArrayBuffer(1024); // 转移 buffer 所有权 iframe.contentWindow.postMessage( { type: 'data', buffer }, 'https://example.com', [buffer] // buffer 被转移 ); console.log(buffer.byteLength); // 0 - 已不可用多个可转移对象
javascript
const buffer1 = new ArrayBuffer(256); const buffer2 = new ArrayBuffer(512); const channel = new MessageChannel(); parentWindow.postMessage( { buffers: [buffer1, buffer2], port: channel.port1 }, '*', [buffer1, buffer2, channel.port1] // 转移所有对象 );重要注意事项
1.所有权转移是永久性的
javascript
const buffer = new ArrayBuffer(1024); postMessage(buffer, '*', [buffer]); // 错误!buffer 已转移,不能再使用 const view = new Uint8Array(buffer); // TypeError
2.只能转移特定的对象类型
尝试转移不可转移的对象会抛出错误:
javascript
const obj = { name: 'test' }; postMessage(obj, '*', [obj]); // TypeError: Value at index 0 is not transferable3.性能优势
转移比复制更高效,特别对于大型数据:
javascript
// 复制方式(占用双倍内存) const largeBuffer = new ArrayBuffer(100 * 1024 * 1024); // 100MB postMessage({ data: largeBuffer }, '*'); // 完整复制 // 转移方式(零拷贝) postMessage(largeBuffer, '*', [largeBuffer]); // 仅转移所有权4.接收方处理
javascript
// 接收方 window.addEventListener('message', (event) => { if (event.data.buffer) { // buffer 的所有权现在属于这个上下文 const view = new Uint8Array(event.data.buffer); // 使用 view... // 可以选择再转移回去 event.source.postMessage('done', '*', [event.data.buffer]); } });实际应用场景
高性能图像处理
javascript
// 主线程 → Worker const offscreen = new OffscreenCanvas(800, 600); const bitmap = offscreen.getContext('2d').getImageData(0, 0, 800, 600); worker.postMessage({ bitmap }, '*', [bitmap]);音频数据处理
javascript
// 处理音频流 const audioBuffer = audioContext.createBuffer(2, 44100, 44100); worker.postMessage({ audio: audioBuffer }, '*', [audioBuffer]);WebGL纹理共享
javascript
// 共享纹理数据 const gl = canvas.getContext('webgl'); const texture = gl.createTexture(); // ...设置纹理数据 offscreenCanvas.transferToImageBitmap(); // 转移到 Worker 进行处理浏览器兼容性
所有现代浏览器都支持基本的
postMessage()可转移对象支持:
ArrayBuffer: Chrome 13+, Firefox 18+, Safari 6+
MessagePort: Chrome 59+, Firefox 59+, Safari 11.1+
ImageBitmap: Chrome 52+, Firefox 51+, Safari 11.1+
最佳实践
始终指定具体的 targetOrigin,避免使用
"*"验证消息来源:
javascript
window.addEventListener('message', (event) => { if (event.origin !== 'https://trusted-domain.com') return; // 处理消息 });及时释放资源:转移后主动清理引用
错误处理:处理转移失败的情况
transfer参数是实现高性能跨线程/跨文档通信的关键特性,特别适用于处理大型二进制数据。
关键要点总结
结构化克隆:默认情况下,postMessage 会对传输的数据进行深拷贝,发送方保留数据副本。
可转移对象:通过 transfer 数组指定的对象会转移所有权,发送方失去访问权限,接收方获得所有权。
性能优势:对于大型数据(如 ArrayBuffer),使用可转移对象可以显著提高性能,避免不必要的数据复制。
适用对象:常见的可转移对象包括 ArrayBuffer、MessagePort、ImageBitmap 等。
使用场景:适用于需要在主线程和 worker 之间高效传输大量数据的情况,例如图像处理、音频数据处理等。
这种方式特别适合需要高性能数据传输的场景,因为它避免了复制大型数据结构的开销。