iframe 子之间通信_iframe与主窗口之间的通信协议

iframe 子之间通信

From time to time, developers need to establish communication between several browser tabs to be able to send messages from one tab to another and receive responses. We have also faced this need at some point.

开发人员有时需要在多个浏览器选项卡之间建立通信,以便能够将消息从一个选项卡发送到另一个选项卡并接收响应。 在某些时候,我们也面临着这种需求。

Some solutions already exist (like, for instance, BroadcastChannel API). However, its browser support leaves a lot to be desired, so we decided to use our own library. When the library was ready, that functionality was no longer required. Nevertheless, another task emerged: communication between an iframe and the main window.

已经存在一些解决方案(例如,BroadcastChannel API)。 但是,它对浏览器的支持还有很多不足之处 ,因此我们决定使用自己的库。 库准备就绪时,不再需要该功能。 尽管如此,另一个任务出现了:iframe和主窗口之间的通信。

On closer examination, it turned out that two-thirds of the library would not have to be changed — only some code refactoring was necessary. The library is a communication PROTOCOL that can work with text data. It can be applied in all cases in which text is transferred, such as iframes, window.open, worker, browser tabs or WebSocket.

经过仔细检查,结果发现不必更改三分之二的库,只需要重构一些代码即可。 该库是可以与文本数据一起使用的通信协议。 它可以应用于所有传输文本的情况,例如iframe,window.open,worker,浏览器标签或WebSocket。

这个怎么运作 (How it works)

Currently, the protocol has two functions: sending messages and subscription to events. Any message in the protocol is a data object. For us, the main field in that object is type, which tells us what kind of message it is. The type field is an enum with the following values:

当前,该协议具有两个功能:发送消息和事件订阅。 协议中的任何消息都是数据对象。 对我们来说,该对象的主要字段是type ,它告诉我们它是哪种消息。 类型字段是具有以下值的枚举

  • 0 — sending a message

    0-发送消息
  • 1 — sending a request

    1-发送请求
  • 2 — receiving a response.

    2-收到回复。

传送讯息 (Sending a message)

Sending a message doesn't imply a response. To send a message, we create an object with the following fields:

发送消息并不意味着响应。 要发送消息,我们创建一个具有以下字段的对象:

  • type — event type 0

    类型 -事件类型0

  • name — user event name

    名称 -用户事件名称

  • data — user data (JSON-like).

    data —用户数据(类似于JSON)。

On receiving a message on the other side with the type field = 0, we know it is an event, with an existing event name and data. All we have to do is broadcast the event (almost a standard EventEmitter pattern).

在另一侧收到类型为 field = 0的消息时 ,我们知道这是一个事件,具有现有的事件名称和数据。 我们要做的就是广播事件(几乎是标准的EventEmitter模式)。

How it works in a simple schema:

它如何在简单模式中工作:

发送请求 (Sending a request)

Sending a request implies that within the library, a request id is created and the library will wait for a response with the id. Upon successfully receiving a response, all auxiliary fields will be removed from it, and the response will be returned to the user. Also, the maximum response time can be set.

发送请求意味着在库中创建了一个请求ID ,并且该库将等待ID为的响应。 成功收到响应后,将从中删除所有辅助字段,并将响应返回给用户。 另外,可以设置最大响应时间。

As for requests, this is a bit more complicated. To respond to a request, you need to announce the methods that are available in our protocol. This is done with the registerRequestHandler method. It accepts the name of a request for a response and a function that returns the response. To create a request, we need an id, and we can basically use timestamp, but it is not very convenient to adjust. So, this is a class ID that sends a response + response number + string literal. Now we create an object with the following fields: id, type = 1, name as request name and data as user data (JSON-like).

至于请求,这有点复杂。 要响应请求,您需要宣布我们协议中可用的方法。 这是通过registerRequestHandler方法完成的。 它接受响应请求的名称和返回响应的函数。 要创建一个请求,我们需要一个id ,我们基本上可以使用timestamp ,但是调整起来不是很方便。 因此,这是一个发送响应+响应编号+字符串文字的类ID。 现在,我们创建一个具有以下字段的对象: idtype = 1name作为请求名称, data作为用户数据(类似于JSON)。

On receiving a request, we check if we have an API for responding to this request, and if we don't, we return an error message. If we have an API, we return the result of executing the function from registerRequestHandler, with the respective request name.

收到请求后,我们将检查是否有用于响应此请求的API,否则,我们将返回错误消息。 如果我们有一个API,则会从registerRequestHandler返回具有相应请求名称的函数执行结果。

For the response, we create an object with the fields: type = 2, id as the ID of the message to which we respond, status as a field that says if this response is an error (if we don't have an API, or the handler incurred an error, or the user returned a rejected promise, or another error occurs (serialise)), and content as response data.

对于响应,我们创建一个具有以下字段的对象: type = 2, id为我们响应的消息的ID, status为表明该响应是否为错误的字段(如果我们没有API,或处理程序发生错误,或者用户返回了被拒绝的承诺,或者发生了另一个错误(序列化)),并将内容作为响应数据。

So, we have described the operation of the protocol, which executes the Bus class but has not explained the process of sending and receiving messages. For that, we need class adapters with three methods.

因此,我们已经描述了协议的操作,该协议执行Bus类,但是没有说明发送和接收消息的过程。 为此,我们需要具有三种方法的类适配器。

  • send is a method that is basically responsible for sending messages

    send是一种基本上负责发送消息的方法

  • addListener is a method for subscribing to events

    addListener是一种订阅事件的方法

  • destroy is a method for deleting subscriptions when deleting Bus.

    destroy是删除Bus时删除订阅的方法。

适配器。 协议的执行 (Adapters. Execution of the protocol)

To launch the protocol, currently, only the adapter for working with iframe/window is ready. It uses postMessage and addEventListener. It's pretty straightforward: you need to send a message to postMessage with a correct origin and listen to messages over addEventListener on the "message" event.

要启动该协议,目前,仅准备好用于iframe /窗口的适配器。 它使用postMessageaddEventListener 。 这非常简单:您需要使用正确的来源将消息发送到postMessage ,并在“ message”事件上通过addEventListener侦听消息。

We encountered a few nuances:

我们遇到了一些细微差别:

  • You should always listen to responses on YOUR window and send them on the OTHER window (iframe, opener, parent, worker, etc). If you try to listen to a message on the OTHER window and the origin differs from the current one, an error will occur.

    您应该始终在您的窗口上收听回复,并在其他窗口(iframe,opener,parent,worker等)上发送回复。 如果您尝试在OTHER窗口上收听一条消息,但其来源与当前消息不同,则会发生错误。
  • On receiving a message, make sure that it was directed to you: the window can accommodate many analytics messages, WebStorm (if you use it) and other iframes, so you need to be sure the event is in your protocol and intended for you.

    接收到消息后,请确保将消息定向到您:该窗口可以容纳许多分析消息,WebStorm(如果使用)和其他iframe,因此您需要确保该事件在协议中并且是为您准备的。
  • You can't return a promise with a Window copy, because promise when returning the result, will try to check if the result has the then method. If you don't have access to the window (for instance, a window with another origin), an error will occur (although not in all browsers). To avoid this issue, it would be enough to wrap the window in the object and put an object into the promise that has a link to the correct window.

    您不能使用Window副本返回诺言 ,因为诺言在返回结果时将尝试检查结果是否具有then方法。 如果您无权访问该窗口(例如,具有其他来源的窗口),则会发生错误(尽管并非在所有浏览器中)。 为避免此问题,将窗口包裹在对象中并将对象放入具有链接到正确窗口的Promise中就足够了。

用法示例: (Usage examples:)

The library is available in NPM and you can easily install it via your package manager — @waves/waves-browser-bus

该库在NPM中可用,您可以通过软件包管理器轻松安装它- @ waves / waves-browser-bus

To establish two-way communication with an iframe, it is enough to use this code:

要与iframe建立双向通信,只需使用以下代码即可:

import { Bus, WindowAdapter } from '@waves/waves-browser-bus';

const url = 'https://some-iframe-content-url.com';
const iframe = document.createElement('iframe');

WindowAdapter.createSimpleWindowAdapter(iframe).then(adapter => {
    const bus = new Bus(adapter);

    bus.once('ready', () => {
        // A message from iframe received 
    });
});
iframe.src = url; // It's preferable to assign a url after calling WindowAdapter.createSimpleWindowAdapter
document.body.appendChild(iframe);

Inside iframe:

在iframe内部:

import { Bus, WindowAdapter } from '@waves/waves-browser-bus';

WindowAdapter.createSimpleWindowAdapter().then(adapter => {
    const bus = new Bus(adapter);

    bus.dispatchEvent('ready', null); // A message has been sent to the parent window
});

下一步是什么? (What's next?)

We have a flexible and versatile protocol that can be used in any situation. Next, I plan to separate the adapters from the protocol and put them into separate npm packages, and add adapters for worker and browser tabs. I want writing adapters executing the protocol for any other purposes to be as easy as possible. If you want to join the development process or have ideas regarding the library's functionality, you are welcome to get in touch in the repo.

我们有一个灵活而通用的协议,可以在任何情况下使用。 接下来,我计划将适配器与协议分开,并将它们放入单独的npm软件包中,并为工作程序和浏览器选项卡添加适配器。 我希望编写用于执行任何其他目的的协议的适配器尽可能地容易。 如果您想参与开发过程或对库的功能有任何想法,欢迎与您联系以进行回购

翻译自: https://habr.com/en/company/waves/blog/456016/

iframe 子之间通信

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值