Muitos desenvolvedores periodicamente precisam estabelecer comunicação entre várias guias do navegador: a capacidade de enviar mensagens de um para outro e receber uma resposta. Tal tarefa surgiu diante de nós.
Existem soluções padrão como o BroadcastChannel, mas agora o suporte ao navegador deixa muito a desejar , por isso decidimos implementar nossa biblioteca. Quando a biblioteca estava pronta, descobriu-se que essa funcionalidade não era mais necessária, mas outra tarefa apareceu: era necessário se comunicar entre o iframe e a janela principal.
Após uma inspeção mais detalhada, verificou-se que dois terços da biblioteca não podem ser alterados ao mesmo tempo, basta refatorar o código um pouco. A biblioteca é um PROTOCOLO de comunicação que pode trabalhar com dados de texto. Pode ser usado em todos os casos, se for possível transferir texto (iframe, window.open, worker, guias do navegador, WebSocket).
Como isso funciona
No momento, o protocolo tem duas funções: enviar uma mensagem e assinar eventos. Qualquer mensagem no protocolo é um objeto com dados. O campo principal desse objeto é o campo de tipo , que nos diz que tipo de mensagem é essa. O campo de tipo é enumerado com valores:
- 0 - enviar mensagem
- 1 - envio de solicitação
- 2 - recebendo uma resposta.
Envio de mensagem
O envio de uma mensagem não implica uma resposta. Para enviar um evento, construímos um objeto com campos:
- tipo - tipo de evento 0
- nome - nome do evento do usuário
- data - dados do usuário (como JSON).
Quando recebemos uma mensagem do outro lado com o campo type = 0, sabemos que este é um evento e que há um nome e dados de evento. Tudo o que resta é acionar o evento (um padrão EventEmitter quase regular).
Esquema de trabalhar com eventos:

Solicitar envio
O envio de uma solicitação implica que um ID de solicitação esteja sendo gerado dentro da biblioteca, a biblioteca aguardará uma resposta com essa ID e, após uma resposta bem-sucedida, os campos de serviço serão excluídos e a resposta será retornada ao usuário. Além disso, você pode definir o tempo máximo de resposta.

Com a solicitação, tudo fica um pouco mais complicado. Para responder a uma solicitação, você deve declarar métodos disponíveis em nosso protocolo. Isso é feito usando o método registerRequestHandler . Ele aceita o nome da solicitação à qual responderá e a função que retorna a resposta. Para criar uma solicitação, precisamos de um ID e, em geral, você pode usar o registro de data e hora , mas é muito inconveniente para depurar. Portanto, esse é o ID da classe que envia a solicitação + número de série da solicitação + constante da string. Em seguida, construímos um objeto com os campos id , tipo - com o valor 1, nome - o nome da solicitação, dados - dados do usuário (como JSON).
Após o recebimento da solicitação, verificamos se temos uma API para responder a essa solicitação; se não houver API, retornamos um erro. Se houver uma API, retornamos o resultado da função de registerRequestHandler , com o nome da solicitação correspondente.
Um objeto é formado para a resposta com campos de tipo - com um valor de 2, id - id da mensagem à qual estamos respondendo, status - um campo que informa se essa resposta é um erro (se não houver API ou ocorreu um erro na saída do usuário ou o usuário retornou a Promessa rejeitada) outros erros (serializar)), conteúdo - dados de resposta.
Assim, descrevemos a operação do próprio protocolo, que implementa a classe Bus , mas não descrevemos como realmente enviar e receber mensagens. Para isso, precisamos de adaptadores - uma classe com 3 métodos:
- send - um método que é realmente responsável por enviar uma mensagem
- addListener - um método para assinar eventos
- destruir - para destruir assinaturas ao destruir o Barramento.
Adaptadores Implementação de protocolo.
Para começar tudo isso, no momento apenas o adaptador para trabalhar com iframe / window está pronto. Funciona em postMessage e addEventListener . Tudo é bem simples aqui: você precisa enviar uma mensagem para postMessage com a origem correta e ouvir mensagens através de addEventListener no evento "message".
Pequenas sutilezas que encontramos:
- Você deve sempre ouvir as respostas na SUA janela e enviá-las para OUTRO (iframe, opener, pai, trabalhador, ...).
O fato é que, quando você tenta ouvir uma mensagem na janela de outra pessoa, se a origem for diferente da atual, ocorrerá um erro. - Ao receber uma mensagem, verifique se ela foi enviada a você (várias mensagens da análise são acionadas na janela,
WebStrom (se você o usar), iframes estrangeiros, portanto, verifique se o evento está em nosso protocolo e para nós). - Você não pode retornar o Promise com uma instância do Window , pois o Promise, ao retornar o resultado, tenta verificar se o resultado possui um método then e se você não tem acesso à janela (uma janela com uma origem diferente, por exemplo), ocorrerá um erro (embora não em todos os navegadores ) Para evitar esse problema, basta quebrar a janela em um objeto e colocar o objeto Promise no qual há um link para a janela desejada.
Exemplos de uso:
A biblioteca pode ser instalada usando seu gerenciador de pacotes favorito - @ waves / waves-browser-bus
Para estabelecer uma comunicação bidirecional com um iframe, basta escrever o 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', () => {
E dentro do iframe:
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?
Acabou sendo um protocolo flexível e universal que pode ser usado em qualquer situação.
Agora, pretendo separar os adaptadores do protocolo e colocá-los em pacotes npm separados, adicionar adaptadores para trabalhar com as guias de trabalho e navegador. Gostaria de escrever adaptadores que implementam o protocolo para quaisquer outras necessidades, foi o mais simples possível.
Se você deseja ingressar no desenvolvimento ou idéias sobre a funcionalidade da biblioteca - você é bem-vindo ao repositório .