message 是浏览器跨文档/跨窗口消息通信的原生事件。

基本概念

// 发送消息
targetWindow.postMessage(data, targetOrigin)

// 接收消息
window.addEventListener('message', (event) => {
  // event 包含三个重要属性
})

event 对象的三个核心属性

属性 说明 示例
event.data 传递的数据 { name: 'spin' }
event.origin 发送方的源(协议+域名+端口) 'https://softool.cn'
event.source 发送方的窗口引用 window, iframe.contentWindow

在 SW 中的应用

// 发送方(可以是任何窗口)
window.opener.postMessage({ name: 'spin' }, '*')

// 接收方(SW.vue)
window.addEventListener('message', (event) => {
  if (event.data?.name === 'spin') {
    triggerSpin()
  }
})

常见使用场景

1. 父子 窗口通信

// 父窗口 → iframe
document.getElementById('myIframe').contentWindow
  .postMessage({ action: 'spin' }, 'https://child.com')

// iframe → 父窗口
window.parent.postMessage({ action: 'done' }, 'https://parent.com')

2. 弹出 窗口通信

// 主窗口 → 弹出窗口
const popup = window.open('https://example.com')
popup.postMessage({ name: 'spin' }, 'https://example.com')

// 弹出窗口 → 主窗口
window.opener.postMessage({ result: 'winner' }, 'https://main.com')

3. 浏览器扩展通信

// 内容脚本 → 后台脚本
chrome.runtime.sendMessage({ action: 'spinWheel' })

安全注意事项

// ❌ 不安全:接受所有来源的消息
window.addEventListener('message', (event) => {
  triggerSpin()  // 任何人都能触发
})

// ✅ 安全:验证消息来源
const ALLOWED_ORIGINS = ['https://softool.cn', 'https://localhost:5173']

window.addEventListener('message', (event) => {
  if (!ALLOWED_ORIGINS.includes(event.origin)) return

  if (event.data?.name === 'spin') triggerSpin()
})

与 SW 的其他触发方式对比

触发方式 使用场景 安全性
点击 Canvas 用户手动操作 ✅ 安全
Ctrl+Enter 键盘快捷键 ✅ 安全
postMessage 跨窗口通信 ⚠️ 需验证 origin
ref.spin() 父组件调用 ✅ 安全

总结: message 事件是浏览器原生的跨窗口通信机制,postMessage 发送,addEventListener('message') 接收。在 SW 中主要用于支持从其他窗口远程触发旋转,使用时务必验证 event.origin 确保安全。