# 浏览器窗口间通信

**localStorage**

一个窗口更新localStorage，另一个窗口监听window对象的”storage”事件，不能跨域

```javascript
// 本窗口的设值代码
localStorage.setItem('msg', 'hi')

// 其他窗口监听storage事件
window.addEventListener("storage", function (e) {
  console.log(e)
  console.log(e.newValue)
})

//window.onstorage = (msg) => {}
```

**WebSocket**

所有的`WebSocket`都监听同一个服务器地址，利用`send`发送消息，利用`onmessage`获取消息的变化，跨窗口、跨浏览器，兼容性最佳，需服务端配合

```javascript
let ws = new WebSocket("ws://localhost:3000/")

socket.addEventListener('open', (event) => {
	socket.send('Hello Server!')
})

socket.addEventListener('message', (event) => {
	console.log('Message from server ', event.data)
})
/*
ws.onopen = function (event) {
  // 或者把此方法注册到其他事件中，即可与其他服务器通信
  ws.send({now : Date.now()})
}
ws.onmessage = function (event) {
  console.log(event.data)
}
*/
```

**postMessage**

可跨域，但是只能跟打开的界面进行通信, 借助iframe 或 `window.open`

`otherWindow.postMessage(message, targetOrigin, [transfer]);`

* otherWindow 其他窗口的一个引用，比如iframe的contentWindow属性、执行window\.open返回的窗口对象、或者是命名过或数值索引的window\.frames
* message 将要发送到其他`window`的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化
* targetOrigin 通过窗口的origin属性来指定哪些窗口能接收到消息事件，其值可以是字符串""（表示无限制）或者一个URI。在发送消息的时候，如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值，那么消息就不会被发送；只有三者完全匹配，消息才会被发送。这个机制用来控制消息可以发送到哪些窗口；例如，当用postMessage传送密码时，这个参数就显得尤为重要，必须保证它的值与这条包含密码的信息的预期接受者的origin属性完全一致，来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口，那么请始终提供一个有确切值的targetOrigin
* transfer 可选 是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方，而发送一方将不再保有所有权

```javascript
/*
 * 弹出页 popup 域名是<http://example.org>
 */

//当A页面postMessage被调用后，这个function被addEventListener调用
function receiveMessage(event) {
	if (event.origin !== 'http://example.com:8080') return
	// event.source 就当前弹出页的来源页面
	// event.data 是 "hello there!"
	// 假设你已经验证了所受到信息的origin (任何时候你都应该这样做), 把event.source
	// 作为回信的对象，并且把event.origin作为targetOrigin
	event.source.postMessage(
		'hi there yourself!  the secret response ' + 'is: rheeeeet!',
		event.origin
	)
}

window.addEventListener('message', receiveMessage, false)
```

**document.cookie**

> 没有事件通知机制，只能在另外一个界面寻轮脏数据检查

在页面A设置一个使用 setInterval 定时器不断刷新，检查 Cookies 的值是否发生变化，如果变化就进行刷新的操作。

由于 Cookies 是在同域可读的，所以在页面 B 审核的时候改变 Cookies 的值，页面 A 自然是可以拿到的。

这样的实现方法相当浪费资源而且不够优雅

**直接引用**

其实就是直接获取对方DOM，适用于两个页面在同一域；可以传递对象数据（对象数据使用 instanceof 做类型判断时有坑）；参考 window\.open

```javascript
// 父页面获取子iframe
document.getElementById('iframe的id').contentWindow.document

// 子iframe获取父页面
window.parent.document
```

**window\.name**

浏览器窗口有window\.name属性。这个属性的最大特点是，无论是否同源，只要在同一个窗口里，前一个网页设置了这个属性，后一个网页可以读取它

父窗口先打开一个子窗口，载入一个不同源的网页，该网页将信息写入window\.name属性

`window.name = data;`

接着，子窗口跳回一个与主窗口同域的网址

`window.location.href = 'http://parent.url.com/xxx.html'`

然后，主窗口就可以读取子窗口的window\.name

`let data = document.getElementById('iframe的id').contentWindow.name`

这种方法的优点是，window\.name容量很大，可以放置非常长的字符串；缺点是必须监听子窗口window\.name属性的变化，影响网页性能

**broadcastchannel 兼容性不好**

```javascript
//a 页面
const bc = new BroadcastChannel('test_channel')
bc.onmessage = msg = {}
//b 页面
const bc = new BroadcastChannel('test_channel')
bc.postMessage('hello')
```

**SharedWorker**

HTML5 中的 Web Worker 可以分为两种不同线程类型，一个是专用线程 Dedicated Worker，一个是共享线程 Shared Worker

* Dedicated Worker直接使用new Worker()即可创建，这种web worker是当前页面专有的
* SharedWorker可以被多个window、标签页、iframe共同使用，但必须保证这些标签页都是同源的(相同的协议，主机和端口号)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jackmeng.gitbook.io/note/frontend/js/liu-lan-qi-chuang-kou-jian-tong-xin.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
