Interação entre dois projetos Front-end utilizando postMessage
17
0

Interação entre dois projetos Front-end utilizando postMessage

Há algum tempo precisei resolver a seguinte questão: Tenho um projeto React embutido com a tag em outro projeto Vue. Como fazer com que o clique de um botão no meu projeto FrontEnd A (React) executar uma ação no projeto FrontEnd B (Vue)?

Matheus Odilon
3 min
17
0
undefined

Há algum tempo precisei resolver a seguinte questão: Tenho um projeto React embutido com a tag <embed /> em outro projeto Vue. Como fazer com que o clique de um botão no meu projeto FrontEnd A (React) executar uma ação no projeto FrontEnd B (Vue)?

Para ser mais explicito o que estava rolando era o seguinte, o meu projeto FrontEnd B é uma web application com uma área logada, porém construímos uma landing page em React para mostrar aos usuários não logados, e essa é o FrontEnd A. 

  • FrontEnd A: Landing Page desenvolvida em React.
  • FrontEnd B: Aplicação desenvolvida em Vue.

Criamos um componente chamado SiteComponent no Front B, e utilizamos a tag embed para carregar o conteúdo do site. Basicamente este era o conteúdo html do component:

<template>
  <v-layout>
    <embed src="https://minha-react-page.com.br/" />
  </v-layout>
</template>

Até aqui, tudo de boa. Porém, aqui é onde mora nosso problema. Em nosso projeto Front A, que é o site, temos um botão de LOGIN e quando o usuário clicar nesse botão o Front B deve mudar a rota e ir para o component Login. E nem o embed, iframe ou qualquer elemento do tipo consegue interceptar essa ação, seja manipulado o html ou qualquer outra coisa do tipo, pois as duas aplicações estão hospedadas em origens diferentes. Por isso, precisamos de um jeito de comunicar a ação do site (Front A) para o Front B, para que ele possa executar a ação de mudar de component.

undefined

E a solução que encontramos foi utilizar o postMessage que pertence ao window.

O postMessage foi criado exatamente para resolver esse problema. Ele é um método seguro de comunicação entre window objects. Quando executado, ele envia uma mensagem de determinada aplicação para um destinatário, e o melhor de tudo é que esse destinatário NÃO PRECISA pertencer ao mesmo domínio do remetente.

Então quando o usuário clicar no botão Login do Front A, ao invés de criar uma "ação fake" para interceptar no Front B, nós enviamos um postMessage com destino ao Front B.

O botão Login no site (Front A), executa está ação:

export default function Header() {
  /**
    * Enviando o postMessage
    */
  const goToLogin = () => {
    window.parent.postMessage('goToLogin', 'https://meu-app-vue.com.br/');
  }

  return (
    <>
      <Action>
        <a href="#" onClick={() => { goToLogin(); }}>
          Entrar
        </a>
      </Action>
    </>;
  )
}

O que estamos fazendo no postMessage?

Estamos enviando dois parâmetros: a message ('goToLogin') e o targetOrigin ('https://app.exemplo.com.br/'). O targetOrigin deve ter o endereção da aplicação "destinatária". 

Pronto! O Front A já está enviando uma mensagem que pode ser escutada pelo Front B. E para escutar essa mensagem devemos utilizar o window.addEventListener no Front B:

private addLoginEventListener() {
  window.addEventListener('message', (event) => {
    const { origin, data } = event;

    if (
       origin === 'https://minha-react-page.com.br'
        && data === 'goToLogin'
      ) {
      this.goToLogin();
    }
  }, false);
}

Pronto!

Agora o FrontEnd B recebe a mensagem enviada pelo FrontEnd A. Adicionamos as validões para garantir que essa mensagem veio de um lugar seguro e tem o mesmo valor que estamos esperando.

Quando o usuário clica no botão Login do Front A, o Front B recebe esse evento e executa a ação!

undefined

Assim ficou o código completo do meu FrontEnd B:

<template>
  <v-layout>
    <embed src="https://minha-react-page.com.br" />
  </v-layout>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Site extends Vue {
  private goToLogin() {
    this.$router.push({
      name: 'login',
    });
  }

  private addLoginEventListener() {
    window.addEventListener('message', (event) => {
      const { origin, data } = event;
      if (
       origin === 'https://minha-react-page.com.br'
        && data === 'goToLogin'
      ) {
        this.goToLogin();
      }
    }, false);
  }

  private created() {
    this.addLoginEventListener();
  }
}
</script>

Esse é um problema chato com uma resposta simples. Espero que esse artigo possa te ajudar!

Caso queira saber mais me chame no Instagram ou Linkedin e me segue lá no GitHub.

Até a próxima.