De tempos em tempos, os desenvolvedores precisam estabelecer comunicação entre várias guias do navegador para poder enviar mensagens de uma guia para outra e receber respostas. Também enfrentamos essa necessidade em algum momento.
Algumas soluções já existem (como, por exemplo, API BroadcastChannel). No entanto, o suporte ao navegador deixa muito a desejar , por isso decidimos usar nossa própria biblioteca. Quando a biblioteca estava pronta, essa funcionalidade não era mais necessária. No entanto, outra tarefa surgiu: a comunicação entre um iframe e a janela principal.
Em uma análise mais aprofundada, verificou-se que dois terços da biblioteca não precisariam ser alterados - apenas uma refatoração de código era necessária. A biblioteca é um PROTOCOLO de comunicação que pode trabalhar com dados de texto. Pode ser aplicado em todos os casos em que o texto é transferido, como iframes, window.open, worker, guias do navegador ou WebSocket.
Como funciona
Atualmente, o protocolo possui duas funções: envio de mensagens e assinatura de eventos. Qualquer mensagem no protocolo é um objeto de dados. Para nós, o campo principal desse objeto é o tipo , que nos diz que tipo de mensagem é. O campo de tipo é uma enumeração com os seguintes valores:
- 0 - enviando uma mensagem
- 1 - enviando uma solicitação
- 2 - recebendo uma resposta.
Enviando uma mensagem
Enviar uma mensagem não implica uma resposta. Para enviar uma mensagem, criamos um objeto com os seguintes campos:
- tipo - tipo de evento 0
- nome - nome do evento do usuário
- data - dados do usuário (como JSON).
Ao receber uma mensagem do outro lado com o campo type = 0 , sabemos que é um evento, com um nome e dados de eventos existentes. Tudo o que precisamos fazer é transmitir o evento (quase um padrão EventEmitter padrão).
Como funciona em um esquema simples:

Enviando uma solicitação
O envio de uma solicitação implica que, dentro da biblioteca, um ID de solicitação seja criado e a biblioteca aguardará uma resposta com a ID . Após receber uma resposta com êxito, todos os campos auxiliares serão removidos e a resposta será retornada ao usuário. Além disso, o tempo máximo de resposta pode ser definido.

Quanto aos pedidos, isso é um pouco mais complicado. Para responder a uma solicitação, você precisa anunciar os métodos disponíveis em nosso protocolo. Isso é feito com o método registerRequestHandler . Ele aceita o nome de uma solicitação de resposta e uma função que retorna a resposta. Para criar uma solicitação, precisamos de um ID e podemos basicamente usar o registro de data e hora , mas não é muito conveniente ajustar. Portanto, este é um ID de classe que envia uma resposta + número da resposta + literal da string. Agora, criamos um objeto com os seguintes campos: id , tipo = 1 , nome como nome da solicitação e dados como dados do usuário (como JSON).
Ao receber uma solicitação, verificamos se temos uma API para responder a essa solicitação e, se não recebermos, retornamos uma mensagem de erro. Se tivermos uma API, retornamos o resultado da execução da função de registerRequestHandler , com o respectivo nome da solicitação.
Para a resposta, criamos um objeto com os campos: type = 2, id como o ID da mensagem à qual respondemos, status como um campo que indica se essa resposta é um erro (se não tivermos uma API, ou o manipulador cometeu um erro, ou o usuário retornou uma promessa rejeitada, ou outro erro ocorre (serialização)) e o conteúdo como dados de resposta.
Portanto, descrevemos a operação do protocolo, que executa a classe Bus, mas não explicou o processo de envio e recebimento de mensagens. Para isso, precisamos de adaptadores de classe com três métodos.
- send é um método que é basicamente responsável pelo envio de mensagens
- addListener é um método para assinar eventos
- destroy é um método para excluir assinaturas ao excluir Bus .
Adaptadores. Execução do protocolo
Para iniciar o protocolo, atualmente, apenas o adaptador para trabalhar com iframe / window está pronto. Ele usa postMessage e addEventListener . É bem simples: você precisa enviar uma mensagem para postMessage com uma origem correta e ouvir mensagens através de addEventListener no evento "message" .
Encontramos algumas nuances:
- Você deve sempre ouvir as respostas na SUA janela e enviá-las na janela OTHER (iframe, opener, pai, trabalhador, etc.). Se você tentar ouvir uma mensagem na janela OTHER e a origem for diferente da atual, ocorrerá um erro.
- Ao receber uma mensagem, verifique se ela foi direcionada a você: a janela pode acomodar muitas mensagens analíticas, WebStorm (se você a usar) e outros iframes; portanto, você precisa ter certeza de que o evento está em seu protocolo e se destina a você.
- Você não pode retornar uma promessa com uma cópia do Windows , porque a promessa ao retornar o resultado tentará verificar se o resultado possui o método then . Se você não tiver acesso à janela (por exemplo, uma janela com outra origem), ocorrerá um erro (embora não em todos os navegadores). Para evitar esse problema, seria suficiente quebrar a janela no objeto e colocar um objeto na promessa que possui um link para a janela correta.
Exemplos de uso:
A biblioteca está disponível no NPM e você pode instalá-lo facilmente através do gerenciador de pacotes - @ waves / waves-browser-bus
Para estabelecer comunicação bidirecional com um iframe, basta usar este código:
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', () => {
Iframe interno:
import { Bus, WindowAdapter } from '@waves/waves-browser-bus'; WindowAdapter.createSimpleWindowAdapter().then(adapter => { const bus = new Bus(adapter); bus.dispatchEvent('ready', null);
O que vem a seguir?
Temos um protocolo flexível e versátil que pode ser usado em qualquer situação. Em seguida, pretendo separar os adaptadores do protocolo, colocá-los em pacotes npm separados e adicionar adaptadores para as guias do trabalhador e do navegador. Quero que os adaptadores de gravação que executam o protocolo para quaisquer outros fins sejam o mais fácil possível. Se você deseja ingressar no processo de desenvolvimento ou ter idéias sobre a funcionalidade da biblioteca, entre em contato com o repo .