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/mywebcompUma continuação do tópico demonstrando como interagir entre componentes através do uso de eventos pode ser encontrada
aqui.