Muitos serviços atuais oferecem diversos produtos que podem ser integrados como um widget no website do cliente. Dentre esse produtos, muitos envolvem recursos de atendimento e FAQ (Intercom, Zendesk), que são plugados na página do cliente, e geralmente são desenvolvidos por um time da empresa que está oferecendo esse serviço como um Software as a Service. Abordaremos aqui alguns problemas e soluções comumente encontrados no desenvolvimento de web widgets que precisam ser plugados em uma página, – e que geralmente, é uma página do cliente -, e também apresentarei um case real e comentários relativos ao desenvolvimento de tais componentes.

Enquanto trabalhava como front-end na Calamar, uma startup inteiramente focada em chatbots, tivemos a demanda de criar um app de mensagens que o cliente pudesse inserir em sua página, e seus usuários, ao acessarem a página, poderiam interagir com um robô que respondia perguntas baseadas em FAQ.

Neste artigo abordaremos problemas envolvidos na criação de um widget web, – com foco na parte front-end da solução -, propor soluções e apresentar comentários sobre escolhas que foram bem-ou-não-sucedidas.

No nosso escopo, o sucesso da solução envolve:

  • Reatividade, utilizando frameworks front-end modernas;
  • Facilidade, fácil instalação na página do cliente (self-checkout) com um simples copiar & colar de um script javascript;
  • Isolamento, do código (javascript e css) client-side da página do cliente, para o código (também client-side) do widget;
  • Escalabilidade, para que milhares de clientes possam usar o mesmo script de carregamento, modificando apenas um ID do cliente;

Isolando o seu código do código do cliente

As páginas dos seus clientes, geralmente, são caóticas. Não há como prever que tipo de ferramentas seu cliente está usando, e essas ferramentas quase sempre afetam todo os componentes da página, seja em estilo (CSS), ou até mesmo javascript, que pode modificar o DOM livremente de acordo com regras definidas pelo próprio cliente. Como separar o código do seu widget (que deve se manter consistente entre todos os seus clientes) do código do seu cliente, que você não conhece (e nem deveria), e que potencialmente pode afetar o widget com experiências indesejadas?

A resposta curta é: IFRAMES. Dentre todas as possibilidades de se implementar de um elemento embed na página, iframes foi a escolha que me deixou, surpreendentemente, mais otimista. Apesar tag da <iframe> parecer uma tecnologia remanescente da época dos dinossauros, há algumas vantagens em utilizá-lo:

  1. Iframes são mais seguros, principalmente porque o JavaScript dentro do iframe está sendo executado no contexto de outra página. É claro que há riscos, mas estamos falando de front-end, vocês já sabem que no mundo front-end nada é realmente seguro :p
  2. Não bloqueia a renderização da página, pois a página pode ser carregada enquanto o conteúdo externo (nosso widget) está sendo carregado.
  3. É a prova de falhas de Javascript na página do cliente. Ao usar um iframe, baseamos a injeção apenas na renderização de HTML, não na execução de JavaScript, logo, os erros do cliente não afetarão o iframe.
  4. Estilização fica isolada. É a nossa própria página, por isso, os únicos estilos aplicáveis ​​são provenientes da nossa própria folha de estilo 🙂 (vamos deixar o assunto de customização para daqui a pouco ;))
  5. Testes descomplicados. Para testar se o widget funciona, criamos uma página fictícia e simplesmente colocamos o iframe lá. Como o conteúdo está em um sandbox, não precisamos nos importar com o que tem nessa página fictícia.

Arquitetando as paradas

Vivemos em um mundo onde sua framework preferida, que você aprendeu a usar mês passado, poderá ficar obsoleta no mês que vem, e isso se dá pela alta rotatividade e constante reinvenção das frameworks JavaScript modernas (tudo bem, não chore, é normal). Mas (felizmente) hoje em dia todas elas seguem tecnologias semelhantes, e como podemos usar nossa a framework preferida com iframes? COMUNIDADES! Seja React, Vue.js, Angular ou Whatever.js que você esteja usando, alguém já disponibilizou um componente que permite encapsular todo o seu aplicativo ou ‘por componente’ em um iFrame. Veja esse essa lib do React como exemplo (que a propósito, foi criada por uns caras do Zendesk, um serviço que também disponibiliza um widget para ser instalado na página do cliente), ou esse fiddle em Vue.js.

No nosso caso, o app consistia em um pequeno overengineering. A arquitetura com iframes foi definida assim:

  1. Um bloco de script para que o cliente insira na página dele (aqui podemos setar algumas variáveis da window, além de termos um link para carregar scripts muito mais complexos que virão a seguir)
  2. Um script para carregar todo o bundle JS em um iFrame dentro da página do cliente (lembra de ser a prova de falhas do javascript do cliente?)
  3. Esse iFrame, que possui só o bundle JS da aplicação, fica encarregado por montar a aplicação no <body> da página do cliente (precisamos de uma <div> encapsulando seus componentes iFrame do app por questões de responsividade e uma ou outra estilização)
  4. Nosso componente iFrame, agora dentro da aplicação utilizando uma dessas libs que comentei acima, que será a área isolada com o chat, botões, e todo o resto da lógica divertida da aplicação

Complicado né? Fazendo um esboço da nossa UI, temos algo como esse wireframe assim:

Nesse widget, temos um botão que controla a abertura e fechamento da janela de chat, e uma janela de chat animada e responsiva.

Você pode ter se perguntado: por que dois iFrames? IFrames conseguem ser bem chatos com mouse events (sério), isto é, a experiência padrão em um iFrame é bloquear os cliques sem passar o evento para elementos do DOM abaixo. Após alguns dias de testes e tentativas, e também por questões de arquitetura por componentes, decidimos utilizar dois iframes para ganhar também esse espacinho clicável na página no cliente.

Para o ponto de entrada de conexão com nosso back-end, utilizamos uma tecnologia de socket chamada Phoenix Channels. Phoenix é um framework de desenvolvimento web escrito em Elixir que implementa padrões MVC. Além disso, ele possui uma feature poderosa chamada Channels, que nos permite adicionar facilmente recursos soft-realtime aos nossos aplicativos, e são baseados em uma ideia simples – enviar e receber mensagens. O Phoenix disponibiliza uma biblioteca JavaScript para fácil implantação do lado do cliente, ou, para nós, implementar uma conexão socket no nosso widget desenvolvido em React. Elixir tem a grande vantagem de ser distribuído, tolerante a falhas e escala muito bem em aplicações que demandam alta conectividade e troca de mensagens, por isso, quando estamos dizendo de uma demanda de milhões de clientes conectados ao mesmo tempo, Elixir destaca-se como um ótimo candidato.

Como tínhamos uma aplicação bem complexa, com cores e nomes customizáveis, imagens de branding, logo do cliente, etc, tudo precisa ser recebido do back-end antes de ser renderizado na página do cliente. Então como persistir essas informações de forma confiável em uma única fonte de verdade no lado do cliente? A resposta é (pasmem): STORES. Redux é incrível. Sua arquitetura one-way dataflow com actions, reducers e store é excelente para guardar o estado de uma aplicação de mensageria como a nossa. E com a ajuda do redux-thunk, podemos passar todas as mensagens recebidas do back-end por reducers, e de acordo com o tipo e payload da mensagem, podemos alterar o estado da nossa aplicação de acordo e de forma consistente na representação da interface.

Conclusões

Fazer um web widget é um desafio de engenharia complexo, envolvendo muitos tópicos de arquitetura tanto de front-end quanto back-end. Segurança, performance e consistência são fatores essenciais em qualquer solução front-end, ainda mais se tratando de uma solução escalável para milhares de clientes e milhões de usuários. No próximo artigo, construiremos um widget chat funcional utilizando ferramentas modernas do mercado, e com isso, poderemos desbravar mais profundamente os problemas e sucessos envolvidos no desenvolvimento de um widget web.

Gabriel Cencic, formado em Física Computacional na USP, trabalha atualmente como Engenheiro Front-End na Wavy Global. Entusiasta em jogos, fã de Adventure Time, robôs e dank memes.

Posted by:Gabriel Cencic

<i>Gabriel Cencic, formado em Física Computacional na USP, trabalha atualmente como Engenheiro Front-End na Wavy Global. Entusiasta em jogos, fã de Adventure Time, robôs e dank memes.</i>

Deixe seu comentário