Início rápido com WebComponents

Os componentes da Web são um conjunto de padrões que definem as interfaces de software para organizar a arquitetura dos componentes. Todos eles são implementados em versões modernas de navegadores, ou seja, eles não requerem conexão de bibliotecas ou transpilers de código; no entanto, se você precisar de compatibilidade, por exemplo, com o Internet Explorer 11, provavelmente ainda precisará usar bibliotecas e transpilers.

Este artigo tem como objetivo o nível inicial de treinamento e desenvolvedores que têm experiência com uma ou outra estrutura front-end, mas talvez, graças a alguns truques, seja interessante para especialistas experientes.

Todas as experiências citadas abaixo foram testadas no Chrome e Firefox podem até não ser as versões mais recentes.

Então, vamos começar.

Primeiro, crie um diretório para o projeto e vá para ele.

mkdir mywebcomp cd mywebcomp 

Execute:

 npm init 

neste diretório, respondendo a todas as perguntas por padrão.

Crie um arquivo index.html com o conteúdo mais simples do diretório.

 <html lang="en"> <body> </body> </html> 

Adicione uma tag para um elemento, o nome deve conter um hífen; este é um sinal para o subsistema CusomElements tentar definir esse elemento como construindo sobre os padrões.

 <html lang="en"> <body> <my-webcomp></my-webcomp> </body> </html> 

Adicione uma classe de manipulador à tag de script .

 <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { this.insertAdjacentHTML('beforeEnd', `<div>Hello</div>`) } } customElements.define('my-webcomp', MyWebComp); </script> 

Na tag de script modular, definimos uma nova classe que, usando o método customElements.define () , a definiu atrás da tag my-webcomp . E adicionando o código ao método connectedCallback () , fornecemos sua chamada ao adicionar a implementação do componente à árvore. O resultado já pode ser visualizado no navegador:



No entanto, colocar o layout html diretamente no código, se for conveniente para peças pequenas, geralmente não é correto, o que é especialmente verdadeiro quando as peças crescem para um tamanho decente. Por exemplo, em um formulário com 20 elementos, o que vencer em subcomponentes também pode nem sempre ser conveniente. Portanto, para iniciantes, apresentaremos o layout no modelo, que estará localizado no mesmo html, embora nada nos impeça de carregá-lo de um arquivo separado, se necessário.

 <html lang="en"> <body> <template id="myWebCompTemplate"> <div>Hello</div> </template> <my-webcomp></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let tplEl = document.querySelector('#myWebCompTemplate'); let html = document.importNode(tplEl.content, true); this.appendChild(html); } } customElements.define('my-webcomp', MyWebComp); </script> </body> </html> 

No código do componente, substituímos a inserção de uma string por html, recebendo um elemento de modelo por id. Importar, ou seja, criando uma cópia desse elemento e vinculando ao conteúdo do atual.

id é nomeado na notação camelCase desde todos os identificadores de elementos são lançados no espaço de nomes global ao usar hífens ou outras promoções. o acesso a caracteres pode ser menos elegante. I.e. nós poderíamos:

  let tplEl = document.querySelector('#myWebCompTemplate'); let html = document.importNode(tplEl.content, true); 

escreva em uma linha:

 let html = document.importNode(myWebCompTemplate.content, true); 

e esse código funcionaria da mesma maneira, mas é considerado não muito seguro. Além disso, se atribuirmos um ID ao nosso elemento, podemos acessá-lo de qualquer lugar do contexto como uma instância do namespace global chamando métodos e obtendo valores de propriedade.
Por exemplo, assim:

 <my-webcomp id="myWebComp"></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.appendChild(html); } } customElements.define('my-webcomp', MyWebComp); </script> <script type="module"> myWebComp.showMessage(); </script> 

Nesse cenário, o alerta será exibido imediatamente quando a página for carregada.

Agora, colocaremos um manipulador de clique do mouse em nosso componente que exibirá uma mensagem de alerta.

 <my-webcomp id="myWebComp" onclick="this.showMessage(event)"></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.appendChild(html); } showMessage(event) { alert("This is the message"); console.log(event); } } customElements.define('my-webcomp', MyWebComp); </script> 

Agora, quando você clicar em uma mensagem, teremos uma reação às ações do usuário.



O argumento para o método showMessage () também declara um objeto de evento que armazena dados do evento, como as coordenadas de um clique ou um link para o próprio elemento.

Freqüentemente, cada elemento específico deve ser configurado de uma maneira única para isso, isso pode ser feito usando atributos.

Adicione uma segunda instância do elemento e defina para cada uma delas diferentes propriedades de nome de saudação cujos valores serão exibidos quando o elemento for clicado.

 <my-webcomp id="myWebComp" greet-name="John" onclick="this.showMessage(event)"></my-webcomp> <my-webcomp id="myWebComp2" greet-name="Josh" onclick="this.showMessage(event)"></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.appendChild(html); } showMessage(event) { alert("This is the message " + this.getAttribute('greet-name')); console.log(event); } } customElements.define('my-webcomp', MyWebComp); </script> 

Agora, quando você clicar no primeiro, “Esta é a mensagem para John” será exibida e no segundo “Esta é a mensagem para Josh”.

Pode acontecer que o atributo precise ser usado não no processamento de eventos, mas diretamente renderizado no modelo. Para isso, adicionaremos o ID ao elemento de destino e substituiremos o valor da API imediatamente após renderizar uma cópia do objeto do modelo.

 <template id="myWebCompTemplate"> <div id="helloLabel">Hello</div> </template> <my-webcomp id="myWebComp" greet-name="John" onclick="this.showMessage(event)"></my-webcomp> <my-webcomp id="myWebComp2" greet-name="Josh" onclick="this.showMessage(event)"></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.appendChild(html); this.querySelector('#helloLabel').textContent += ' ' + this.getAttribute('greet-name'); } showMessage(event) { alert("This is the message " + this.getAttribute('greet-name')); console.log(event); } } customElements.define('my-webcomp', MyWebComp); </script> 

Acontece assim:



Em vez de .textContent, pode ser .innerHTML ou você pode chamar o mesmo método .insertAdjacentHTML () no objeto do seletor que fizemos no início.

Por um longo tempo, o uso de IDs foi considerado ruim, porque em quantidades significativas de código eles poderiam ser duplicados, o que levou a colisões. No entanto, com o advento da tecnologia de árvore de sombra, você pode isolar o conteúdo interno de um elemento usando identificadores, estilos e outros recursos sem medo. Para componentes da Web, a árvore de sombra é ativada da seguinte maneira:

  this.attachShadow({mode: 'open'}); 

Agora, a verdade é que todas as chamadas do DOM para isso terão que ser substituídas por this.shadowRoot, já que não existem muitas.

 <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(html); this.shadowRoot.querySelector('#helloLabel').textContent += ' ' + this.getAttribute('greet-name'); } showMessage(event) { alert("This is the message " + this.getAttribute('greet-name')); console.log(event); } } customElements.define('my-webcomp', MyWebComp); </script> 

Visualmente, o trabalho desse código não será alterado novamente, mas agora não haverá helloLabel no espaço de nomes global e a página já possui 2 elementos com esse identificador. E você pode acessá-los, por exemplo, assim:

 myWebComp.shadowRoot.getElementById('helloLabel'); 

e se você não fechar a árvore, passe o atributo correspondente no método .attachShadow () .

Temos bastante código e colocá-lo diretamente no arquivo html também não é muito correto. Portanto, criaremos o arquivo my-webcomp.js e transferiremos nossa classe para ele com a instrução de exportação e adicionaremos a importação dessa classe na tag de script para obter isso:

 <script type="module"> import { MyWebComp } from "./my-webcomp.js"; customElements.define('my-webcomp', MyWebComp); myWebComp.shadowRoot.getElementById('helloLabel'); </script> 

Isso não afetará o desempenho, mas agora toda a nossa lógica de negócios está separada em .js, a configuração é realizada em atributos html e os sistemas de navegação modular e de componentes cuidam da inicialização assíncrona modular.

No entanto, a partir de agora, a abertura de index.html como local para desenvolvimento falhará, porque o navegador bloqueará o download do script do sistema de arquivos. Se você possui nodejs, pode colocar o servidor da Web mais simples:

 npm i http-server -g 

e execute-o com o comando http-server no diretório do projeto; na inicialização, ele informará o host e a porta a partir da qual você pode abrir a página

 http://127.0.0.1:8080 

Que agora será o endereço da página de depuração com o nosso elemento.

Escrever testes também é importante para o desenvolvimento, pois ajuda a manter o código viável durante o desenvolvimento de seus componentes. Para testar componentes da web, você pode usar o mocha no modo de inicialização no navegador.

Adicione pacotes.

 npm i mocha chai wct-mocha --save-dev 

Para fazer isso, crie o diretório de teste e adicione o seguinte arquivo all.html :

 <!doctype html> <html> <head> <meta charset="utf-8"> <script src="../node_modules/mocha/mocha.js"></script> <script src="../node_modules/chai/chai.js"></script> <script src="../node_modules/wct-mocha/wct-mocha.js"></script> </head> <body> <script> WCT.loadSuites([ 'my-webcomp.tests.html', ]); </script> </body> </html> 

Copie nosso index.html para testar / dar o nome my-webcomp.tests.html e adicione o mesmo conteúdo principal que em all.html .

No entanto, agora precisaremos substituir a inicialização e as manipulações usuais pelas executadas como parte da execução do teste:

 <script type="module"> import { MyWebComp } from "../my-webcomp.js"; customElements.define('my-webcomp', MyWebComp); suite('MyWebComp', () => { test('is MyWebComp', () => { const element = document.getElementById('myWebComp'); chai.assert.instanceOf(element, MyWebComp); }); }); </script> 

Agora, ao entrar

 http://127.0.0.1:8080/test/all.html 

Um relatório de teste será exibido.



Mas pode ser necessário executar os testes automaticamente e em diferentes navegadores, você precisa instalar um utilitário especial:

 sudo npm install --global web-component-tester --unsafe-perm 

e execute assim:

 wct --npm 

Esta linha pode ser adicionada à seção de teste do arquivo package.json e executada como:

 npm test 



Recomenda-se testar cada método significativo de classe como parte de um teste separado. Se for necessário fazer uma inicialização comum para cada uma, ela deve ser definida no método suiteSetup () :

 suiteSetup(() => { }); 

dentro da suíte.

Provavelmente é o suficiente pela primeira vez, temos um projeto mínimo de que, se ele resolvesse uma tarefa específica, não teria vergonha de fazê-lo.

 npm publish 

ou pelo menos

 git push 

Ainda não existem muitos livros sobre o assunto, mas toda a documentação extensa é facilmente encontrada de acordo com Componentes da Web, CustomElements, ShadowDOM, Tag de Modelo Nativo, Eventos Personalizados.

Você pode verificar seu código no repositório: https://bitbucket.org/techminded/mywebcomp

Uma continuação do tópico demonstrando como interagir entre componentes através do uso de eventos pode ser encontrada aqui.

Source: https://habr.com/ru/post/pt460397/


All Articles