前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >web 通信--跨文档、worker、通道

web 通信--跨文档、worker、通道

作者头像
奋飛
发布2022-01-24 10:05:46
7320
发布2022-01-24 10:05:46
举报
文章被收录于专栏:Super 前端Super 前端

跨文档通信(cross-document messaging)、worker通信(cross-worker messaging)、通道通信(channel messaging)

MessageEvent

消息事件 MessageEvent() 用于:

属性:

属性

说明

data

包含任意字符串数据,由原始脚本发送

origin

一个字符串,包含原始文档的方案、域名以及端口(如:http://domain.example:80)

lastEventId

一个字符串,包含了当前的消息事件的唯一标识符。

source

原始文件的窗口的引用。更确切地说,它是一个WindowProxy对象。

ports

一个数组,包含任何MessagePort对象发送消息。

跨文档通信

最常见的例子 iframe 之间。

代码语言:javascript
复制
otherWindow.postMessage(message, targetOrigin, [transfer])

window.addEventListener("message", (messageEvent) => {}, false)

targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。

在这里插入图片描述
在这里插入图片描述

主页面

代码语言:javascript
复制
<iframe src="iframe_1.html">iframe>
<iframe src="iframe_2.html">iframe>

iframe_1.html

代码语言:javascript
复制
<input id="data" />
<button id="btn">发送button>

<script>
  let btn = document.querySelector('#btn')
  btn.addEventListener('click', function () {
    let data = document.querySelector('#data')
    window.parent.frames[1].postMessage(data.value, '*')
    return false
  })
script>

iframe_2.html

代码语言:javascript
复制
接收到的数据:<span id="message">span>

<script>
  let msg = document.querySelector('#message')
  window.addEventListener('message', (msgEvent) => {
    msg.innerHTML = msgEvent.data
  })
script>

worker 通信

一种可由脚本创建的后台任务,任务执行中可以向其创建者收发信息。

代码语言:javascript
复制
worker.postMessage(aMessage, transferList)

worker.onmessage = (msgEvent) => {}	// ①
worker.addEventListener('message', (msgEvent) => {}) // ②
在这里插入图片描述
在这里插入图片描述

主页面

代码语言:javascript
复制
const worker = new Worker('worker.js')
worker.postMessage({
  num1: num1.value,
  num2: num2.value
})
worker.onmessage = function (msgEvent) {
  dom.innerHTML = msgEvent.data
}

worker.js

代码语言:javascript
复制
onmessage = function (msgEvent) {
  let {num1, num2} = msgEvent.data
  postMessage(Number(num1) + Number(num2))
}
可以通过函数方式(非文件)
代码语言:javascript
复制
function createWorker(workerFunc) {
  if (! (workerFunc instanceof Function)) {
    throw new Error('Argument must be function');
  }
  const src = `(${workerFunc})();`;
  const blob = new Blob([src], {type: 'application/javascript'});
  const url = URL.createObjectURL(blob);
  return new Worker(url);    
}

通道通信

MessageChannel 接口允许我们创建一个新的消息通道,并通过它的两个MessagePort 属性发送数据。

在这里插入图片描述
在这里插入图片描述

实现上述双 iframe 示例:

Port1[iframe2] <===> Port2[iframe1]

代码语言:javascript
复制
const [ifr1, ifr2] = document.querySelectorAll('iframe')
const ifr1Window = ifr1.contentWindow
const ifr2Window = ifr2.contentWindow

// 监听【iframe】onmessage 信息
ifr1Window.addEventListener("message", function (msgEvent) {
  ifr1Window.document.querySelector('#btn').addEventListener('click', function () {
    // 根据【port2】进行传值
    msgEvent.ports[0].postMessage(ifr1Window.document.querySelector('#data').value)
  })
})

ifr2Window.addEventListener("load", function () {
  // 建立信息通道
  const channel = new MessageChannel()
  // 监听【port1】的信息
  channel.port1.onmessage = function (msgEvent) {
    ifr2Window.document.querySelector('#message').innerHTML = msgEvent.data
  }
  // 给 iframe1 发送信息【携带 port2 】
  ifr1Window.postMessage('I am ready', '*', [channel.port2])
})
iframe 传值改造

可以实现上述多 iframe 之间传值,更重要的是其可以实现多 web worker 之间传值。

主页面

代码语言:javascript
复制
const channel = new MessageChannel()
const w1 = new Worker('worker1.js')
const w2 = new Worker('worker2.js')

w1.postMessage('index', [channel.port1])
w2.postMessage('', [channel.port2])

w2.onmessage = function (msgEvent) {
  console.log(msgEvent.data)
}

worker1.js

代码语言:javascript
复制
onmessage = function ({data, ports}) {
  const port1 = ports[0]
  port1.postMessage(`${data} => worker1`)
}

worker2.js

代码语言:javascript
复制
onmessage = function({ports}) {
  const port2 = ports[0]
  port2.onmessage = function (msgEvent) {
    postMessage(`${msgEvent.data} => worker2`)
  }
}

index => worker1 => worker2

  • index:① 通过 worker1/2 下发 port1/2;② 监听 worker2。
  • worker1:① 监听worker1;② port1 发送信息。
  • worker2:① 监听worker2;② 监听 port2;③ worker 发送信息。
comlink

Comlink 采用的 RPC 代理方式,并不是传递上下文环境(因为这是非常危险的,而且函数传递时会导致 Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'Worker': xxx could not be cloned. 报错)。

本质上依然是 MessagePort 消息通讯,不过封装了我们所头疼的“操作判断”,并以一种更优雅的方式(Proxy + Promise)来处理。

RPC:Remote Procedure Call,远程过程调用,指调用不同于当前上下文环境的方法,通常可以是不同的线程、域、网络主机,通过提供的接口进行调用。

index.html

代码语言:javascript
复制
import * as Comlink from "https://unpkg.com/comlink/dist/esm/comlink.mjs"

const worker = new Worker("worker.js")
const cw = Comlink.wrap(worker)
comput.addEventListener('click', async function () {
  let result = await cw(num1.value, num2.value)
  dom.innerHTML = result
})

worker.js

代码语言:javascript
复制
importScripts("https://unpkg.com/comlink/dist/umd/comlink.js")
function add (num1, num2) {
  return Number(num1) + Number(num2)
}
Comlink.expose(add)

importScripts() 将一个或多个脚本同步导入到工作者的作用域中。隶属于:WorkerGlobalScope 接口。

参考链接

  • https://developer.mozilla.org/zh-CN/docs/Web/API/MessageEvent
  • https://www.zhangxinxu.com/wordpress/2012/02/html5-web-messaging-cross-document-messaging-channel-messaging/
  • https://developer.mozilla.org/zh-CN/docs/Web/API/MessageChannel
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-01-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MessageEvent
  • 跨文档通信
  • worker 通信
    • 可以通过函数方式(非文件)
    • 通道通信
      • iframe 传值改造
        • comlink
        • 参考链接
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档