Web Components: uma introdução
Tags: javascript, beginners
Published: 2021-07-13 12:27:55

Imagine um projeto web que mostre os dados dos usuários em um componente cartão que será usado em várias páginas no projeto:

![Card do usuário](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ymu8qtffmms3ag7zbps.png)

Ao invés de copiar e colar este código em vários arquivos HTML diferentes, podemos criar nossa própria tag que renderiza este cartão e encapsula os estilos (CSS) e comportamentos (JavaScript). 

Primeiro, criamos o arquivo `UserCard.js` que conterá o código JavaScript deste componente e criamos uma classe representando este componente:

```javascript
// arquivo UserCard.js

class UserCard {
}
```

Até aqui, esta é apenas a declaração de uma classe em JavaScript. 

## Custom Elements

Como queremos criar uma tag, devemos definí-la como um **elemento HTML**. Para isto, basta fazer nossa classe implementar a interface `HTMLElement`:

```javascript
// arquivo UserCard.js

class UserCard extends HTMLElement {
}
```

[`HTMLElement`](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) é uma interface que implementa outra chamada [`Element`](https://dom.spec.whatwg.org/#interface-element) - que é a interface base mais geral da qual todos os objetos em um *Document* implementam. De acordo com a documentação, se queremos criar uma tag, o mais indicado é usar `HTMLElement`, já que esta provê todos os recursos necessários para construção de uma tag HTML.

Após isso, colocamos o construtor e chamamos o `super()` da interface `HTMLElement`:

```javascript
// arquivo UserCard.js

class UserCard extends HTMLElement {

    constructor() {
        super();
    }

}
```

E, por último, precisamos registrar nossa tag no [`CustomElementRegistry`](https://html.spec.whatwg.org/multipage/custom-elements.html#customelementregistry) - que está disponível globalmente pela variável `customElements` e permite registrar um elemento customizado em uma página:

```javascript
// arquivo UserCard.js

class UserCard extends HTMLElement {

    constructor() {
        super();
    }

}

customElements.define("user-card", UserCard);
```

O método `define()` de `customElements` recebe como parâmetro o nome da tag a ser definida e o objeto que vai encapsular o código necessário para sua construção. O nome da tag exige o caractere "-" (traço). Caso este padrão não seja seguido e o nome da tag seja definido, por exemplo, como `usercard`, receberemos um `DOMException` no momento de usar a tag:

```javascript
Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': "usercard" is not a valid custom element name
```

Por este motivo manteremos o nome como `user-card`. E para usar nossa nova tag, devemos importá-la em um arquivo HTML e utilizar com a mesma sintaxe de uma tag comum:

```html
<!-- arquivo index.html -->

<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <h2>Web Components</h2>

        <user-card></user-card>

        <script src="UserCard.js"></script>
    </body>
</html>
```

Como nossa tag não faz nada até o momento, nada vai aparecer no navegador além da frase "Web Components" ao abrir o arquivo `index.html`. Todo elemento HTML tem a propriedade [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) que corresponde ao seu conteúdo. Para vermos algum resultado, vamos sobrescrever esta propriedade com algum conteúdo - por exemplo, com o nome do usuário do componente cartão que estamos desenvolvendo:

```javascript
// arquivo UserCard.js

class UserCard extends HTMLElement {

    constructor() {
        super();
        this.innerHTML = "<h2>Fulano de Tal<h2>"
    }

}

customElements.define("user-card", UserCard);
```

Que vai gerar o resultado:

![Resultado da página renderizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bly3nkees0x4cu5ivt1v.png)

## Templates

Nossa tag customizada, apesar de simples, já funciona como esperado. Agora vamos utilizar e entender um pouco sobre outro recurso bastante utilizado quando trabalhamos com componentes web que são os `Templates`.

Com templates, é possível definir blocos de códigos reutilizáveis. Apesar de já conseguirmos isso sem eles, os templates apresentam uma forma mais racional de fazer isso.

Suponha que queremos repetir o uso de nosso componente várias vezes na página. Seriam muitas chamadas de `this.innerHTML = "<h2>Fulano de Tal</h2>"`. Ou seja, construiria esse elemento várias vezes, sendo que uma única vez já seria necessário. 

Ao invés de adicionarmos o conteúdo com `innerHTML` toda vez que o objeto é construído, podemos usar templates. Como dito na documentação do [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template): *O  elemento HTML `<template>` é um mecanismo para encapsular um conteúdo do lado do cliente que não é renderizado quando a página é carregada, mas que pode ser instanciado posteriormente em tempo de execução usando JavaScript.*

Portanto, ao criarmos algum conteúdo dentro da tag `<template>`, este conteúdo não é mostrado imediatamente. Mas pode ser clonado para ser posteriormente renderizado:

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = `<h2>Fulano de Tal</h2>`;

class UserCard extends HTMLElement {

    constructor() {
        super();
        // código removido
    }
}

customElements.define("user-card", UserCard);
```

Note que criamos o template fora da classe. Agora será preciso clonar o conteúdo deste template que está disponível pelo atributo `content`. E para clonar o conteúdo, utilizamos o método `cloneNode()`:

```javascript
template.content.cloneNode(true)
```

O método [`cloneNode()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode) recebe um parâmetro booleano para indicar se os elementos filhos do nó que está sendo clonado devem ser clonados juntos ou não. Vamos definir com o valor `true` para clonar os filhos também.

Agora devemos pegar este elemento clonado e adicionar em nosso componente através do método [`appendChild()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild):

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = `<h2>Fulano de Tal</h2>`;

class UserCard extends HTMLElement {

    constructor() {
        super();
        this.appendChild(template.content.cloneNode(true));
    }
}

customElements.define("user-card", UserCard);
```

Esta técnica reduz o custo de análise do HTML porque o conteúdo do modelo é analisado apenas uma vez pelo [`DOMParser`](https://w3c.github.io/DOM-Parsing/#the-domparser-interface), enquanto que chamar `innerHTML` dentro do construtor irá analisar o HTML para cada instância. Isso garante uma melhoria na performance de nosso componente.

## Atributos

E se quisermos que cada componente que será renderizado na página tenha um conteúdo diferente? Podemos, como qualquer tag HTML, definir atributos. Por exemplo:

```html
<!-- arquivo index.html -->

<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <h2>Web Components</h2>

        <user-card name="Fulano de Tal"></user-card>
        <user-card name="Ciclano de Tal"></user-card>

        <script src="UserCard.js"></script>
    </body>
</html>
```

O atributo `name` é definido por nós e pode ter o nome que considerarmos conveniente. Neste momento, nosso template está com um conteúdo fixo e devemos modificar de acordo com atributo `name` recebido pela nossa tag. 
 
```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = `<h2></h2>`;

class UserCard extends HTMLElement {

    constructor() {
        super();
        this.appendChild(template.content.cloneNode(true));
        this._name = this.getAttribute("name");
        this.querySelector("h2").textContent = this._name;
    }
}

customElements.define("user-card", UserCard);
```

Como nosso componente é um `HTMLElement`, podemos usar e abusar de todos os recursos que uma tag comum do HTML possui, como o método [`getAttribute()`](https://developer.mozilla.org/en-US/docs/Web/API/element/getAttribute) para pegar o valor do atributo `name` que definimos anteriormente. E teremos o resultado:

![Resultado da página renderizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xehjqqstppc41ncf6xp.png)

## Shadow DOM

Agora que aprendemos um pouco sobre templates, vamos adicionar um estilo para nosso componente. Primeiro, vamos adicionar um estilo para a tag `h2` diretamente no arquivo `index.html`:

```html
<!-- arquivo index.html -->

<html>
    <head>
        <meta charset="UTF-8">
        <style>
            h2 {
                color: red;
            }
        </style
    </head>
    <body>
        <h2>Web Components</h2>

        <user-card name="Fulano de Tal"></user-card>
        <user-card name="Ciclano de Tal"></user-card>

        <script src="UserCard.js"></script>
    </body>
</html>
```

E vamos obter o seguinte resultado:

![Resultado da página renderizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kusxluccnucyfel8qwln.png)

Como todos os elementos da página, inclusive de nosso componente, estão dentro de uma tag `h2`, todos receberão o estilo global. Mas podemos adicionar um estilo específico para nosso componente, modificando a cor para azul, por exemplo. Podemos adicionar a tag `<style>` em nosso template:

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = `
    <style>
        h2 {
            color: blue;
        }
    </style>
    <h2></h2>`;

class UserCard extends HTMLElement {

    constructor() {
        super();
        this.appendChild(template.content.cloneNode(true));
        this._name = this.getAttribute("name");
        this.querySelector("h2").textContent = this._name;
    }
}

customElements.define("user-card", UserCard);
```

Agora temos dois estilos na página para tag `h2`, o estilo global dentro do arquivo `index.html` e o estilo dentro de nosso componente. Qual será que vai ser aplicado em cada caso? Ao renderizar a página, obtemos:

![Resultado da página renderizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/msv3z9nq62mze196qo25.png)

Repare que o estilo de nosso componente foi aplicado também para o conteúdo da tag `h2` fora dele. Isso acontece porque o template com o estilo de nosso componente é carregado por último e acaba por sobrescrever o estilo da tag `h2` externo.

Você pode argumentar que podemos evitar isso utilizando classes CSS e você está totalmente correto! Mas imagine o cenário de um projeto grande em que cada desenvolvedor é responsável por um componente específico. Há grandes chances de nomes iguais de classes CSS serem utilizadas, e isso pode gerar um grande transtorno.

Para evitar este tipo de conflito que trabalharemos com outro recurso chamado de `Shadow DOM`. A ideia é encapsular código HTML, CSS e JavaScript de nosso componente para não provocar e/ou sofrer alteração externa.

O `Shadow DOM` é uma sub árvore do DOM que tem seu próprio escopo e que não faz parte do DOM original, tornando possível construir interfaces modulares sem que entrem em conflito uma com a outra.

<figure>
  <img
  src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1emfdfo36wrpt0hkuyme.png" alt="Shadow DOM">
  <figcaption>fonte da imagem: MDN Web Docs</figcaption>
</figure>

Como especificado no [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM), existem algumas terminologias do `Shadow DOM` que devemos conhecer:

- **Shadow host**: o nó DOM regular ao qual o Shadow DOM está anexado.
- **Shadow tree**: a árvore DOM dentro do Shadow DOM.
- **Shadow boundary**: o lugar onde termina o Shadow DOM e começa o DOM regular.
- **Shadow root**: o nó raiz da Shadow tree.

Dito isto, vamos ver como funciona na prática. Iremos isolar nosso componente dentro de um `Shadow DOM`. Para isso, precisamos criar o nó raiz `Shadow Root` dentro de nosso componente - que será o `Shadow Host`. A classe `HTMLElement` possui o método [`attachShadow()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow) que podemos utilizar para abrir e criar uma referência para um `Shadow Root`. 

Um `Shadow Root` possui dois modos: aberto e fechado. Antes de entrarmos nas diferenças entre esses dois modos, iremos criar nosso `Shadow Root` no modo aberto para ver como ele funciona. O método `attachShadow()` exige que passemos o modo como parâmetro:

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = `
    <style>
        h2 {
            color: blue;
        }
    </style>
    <h2></h2>`;

class UserCard extends HTMLElement {

    constructor() {
        super();
        this.attachShadow({mode: 'open'}); // criando o Shadow Root
        this.appendChild(template.content.cloneNode(true));
        this._name = this.getAttribute("name");
        this.querySelector("h2").textContent = this._name;
    }
}

customElements.define("user-card", UserCard);
```
Após esta mudança, ao renderizar novamente a página, vemos que nosso componente não é renderizado e volta a receber o estilo global definido para a tag `h2`:

![Resultado da página renderizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5fbv4y69y3f3f3z37oyp.png)

Mas é possível verificar que o `Shadow Root` foi criado inspecionando a página através da ferramenta `DevTools` do navegador pela aba `Elemets`:

![Shadow Root](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6yn9xtjugolag689m8jz.png)

Note que o conteúdo do template também foi anexado a tag `<user-card>` mas não é mostrado já que está fora do `Shadow Root`. Uma vez aberto o `Shadow Root`, devemos anexar os conteúdos, como nosso template, dentro dele. Após a chamada do método `attachShadow()`, uma referência ao objeto `Shadow Root` aberto é disponibilizado através do atributo `shadowRoot`:

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = `
    <style>
        h2 {
            color: blue;
        }
    </style>
    <h2></h2>`;

class UserCard extends HTMLElement {

    constructor() {
        super();
        this.attachShadow({mode: 'open'});
        this.shadowRoot.appendChild(template.content.cloneNode(true));  // código modificado
        this._name = this.getAttribute("name");
        this.shadowRoot.querySelector("h2").textContent = this._name; // código modificado
    }
}

customElements.define("user-card", UserCard);
```

Agora, nosso componente é renderizado como antes porque foi anexado ao `Shadow Root`, vamos novamente inspecioná-lo pela ferramenta `DevTools`:

![Shadow Root](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rptcbiphu6599qolclj0.png)

Note que agora o conteúdo está dentro do `Shadow Root`. E como ele está dentro de uma `Shadow Tree` separada do DOM original, os estilos globais não afetam nosso componente e o resultado da renderização da página é este:

![Resultado da página renderizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x2wxhjtwcqtbx3k0m102.png)

Este foi um exemplo utilizado para encapsular os estilos. Mas o mesmo vale para eventos que possam ser registrados em nosso componente - como um evento de *click* que pode afetar muitos elementos de uma página e o `Shadow DOM` vai garantir o encapsulamento. 

Agora que vimos um pouco como o `Shadow DOM` funciona, vamos entender a diferença entre os modos aberto e fechado. O `Shadow Root` no modo aberto permite que façamos modificações em sua estrutura utilizando JavaScript. Se quisermos acessar o `Shadow Root` de nosso componente, basta digitar no console:

```javascript
document.querySelector("user-card").shadowRoot
```
Isso nos permite acessar o `shadowRoot` de nosso componente:

![Shadow Root](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oj0zeb4j5dkh1pmzejnc.png)

E fazer modificações em seu conteúdo, como modificar o conteúdo da tag `h2` de nosso componente:

![Shadow Root](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/23c9o0nt171z8lozu2vk.png)

Repare que o encapsulamento, neste sentido, é quebrado já que podemos modificar sua estrutura através do JavaScript. Para que o encapsulamento seja realmente aplicado é que existe o modo fechado do `Shadow DOM`. Vamos modificar nosso componente para o modo fechado:

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = `
    <style>
        h2 {
            color: blue;
        }
    </style>
    <h2></h2>`;

class UserCard extends HTMLElement {

    constructor() {
        super();
        this.attachShadow({mode: 'closed'});  // modificado para o modo fechado
        this.shadowRoot.appendChild(template.content.cloneNode(true));
        this._name = this.getAttribute("name");
        this.shadowRoot.querySelector("h2").textContent = this._name;
    }
}

customElements.define("user-card", UserCard);
```

Mas, ao fazer isso, nosso componente nem sequer é renderizado:

![Resultado da página renderizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5fbv4y69y3f3f3z37oyp.png)

Isso acontece porque o acesso ao atributo `shadowRoot` não é mais possível. `this.shadowRoot` agora vai retornar `null` e receberemos o seguinte erro no console:

![Console JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o259rdozk7pi3epxbjck.png)

Portanto, não será mais possível acessar o `shadowRoot` externamente pelo JavaScript:

![Shadow Root](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cdy1517lgq42homspa9q.png)

Só será possível fazê-lo dentro de nosso componente. Para isso, criaremos uma referência para ele e assim será possível manipulá-lo e clonar o template para que seja renderizado na página:

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = `
    <style>
        h2 {
            color: blue;
        }
    </style>
    <h2></h2>`;

class UserCard extends HTMLElement {

    constructor() {
        super();
        this._shadowRoot = this.attachShadow({mode: 'closed'});
        this._shadowRoot.appendChild(template.content.cloneNode(true));
        this._name = this.getAttribute("name");
        this._shadowRoot.querySelector("h2").textContent = this._name;
    }
}

customElements.define("user-card", UserCard);
```

Dessa maneira, nosso componente é renderizado como antes:

![Resultado da página renderizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l1x2yjzapmwgmcgmcjtz.png)

E o acesso ao `shadowRoot`, via JavaScript, continua retornando `null`:

![Console JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bmqp405uzl595q2a0e01.png)

Agora temos nosso componente encapsulado e fechado para modificações externas com JavaScript. É evidente que ainda podemos acessá-lo da seguinte forma: 

![Console JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2s753ehirnsc7ourackv.png) 

Mas, seguindo as boas práticas da linguagem, isso deve ser evitado já que indica que este atributo é privado e não deve ser acessado fora da classe `UserCard`.

## Isolando o CSS

Escrever código CSS dentro de um *template string* não é o ideal. O melhor seria se o código CSS de nosso componente ficasse em um arquivo externo de estilos. 

Primeiro, vamos criar o arquivo `UserCard.css`. 

```css
/* arquivo UserCard.css */

h2 {
    color: blue;
}
```

Em seguida, modificamos nosso componente para utilizar este arquivo CSS - importando o arquivo através da tag `<link>`:

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = ` 
    <link type="text/css" rel="stylesheet" href="UserCard.css"></link>
    <h2></h2>`;

class UserCard extends HTMLElement {
    // código omitido
}

customElements.define("user-card", UserCard);
```

Também é possível utilizar o recurso de [regra atribuída](https://developer.mozilla.org/en-US/docs/Web/CSS/@import) do CSS através do `@import`:

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = ` 
    <style>@import url("UserCard.css")</style>
    <h2></h2>`;

class UserCard extends HTMLElement {
    // código omitido
}

customElements.define("user-card", UserCard);
```

Mas como comentado no [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM#internal_versus_external_styles), o carregamento de um estilo externo feito dessa maneira dentro do `ShadowRoot` pode causar o temido FOUC ([*Flash of Unstyled Content*](https://en.wikipedia.org/wiki/Flash_of_unstyled_content)) - ou seja, pode ocorrer um *flash* do conteúdo não estilizado enquanto o CSS é carregado. 

Por este motivo, muitos desenvolvedores mantêm o conteúdo dos estilos dentro da tag `<style>` no template *string* ao invés de tentar evitar FOUC com código adicional - até o momento não existe uma maneira fácil e rápida de evitar isso. 

Por facilidade e para evitar este tipo de problema, optaremos por manter o código de estilos dentro do template *string*, utilizando a tag `<style>`.

## Finalizando o componente do cartão

Agora que entendemos um pouco sobre componentes, podemos voltar ao nosso objetivo final que era criar o componente de cartão de usuários. Basta refatorarmos o código modificando o template de nosso componente e fazendo os ajustes em seu construtor. O código final ficaria assim:

```html
<!-- arquivo index.html -->

<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <h2>Web Components</h2>

        <user-card name="Fulano de Tal" job="Desenvolvedor de Software" image="user.png"></user-card>

        <script src="UserCard.js"></script>
    </body>
</html>
```

```javascript
// arquivo UserCard.js

const template = document.createElement('template');
template.innerHTML = `
    <style>
        .card {
            font-family: Arial;
            border: 1px solid #c5c9d1;
            border-radius: 4%;
            width: 150px;
            height: 60px;
            display: flex;
            color: #5b6069;
            font-size: 12px;
            padding: 10px;
        }

        .card:hover {
            background-color: hsl(0, 0%, 97%);
        }

        .card-image,
        .card-content {
            padding: 5px;
        }

        .user-image {
            width: 45px;
            height: 45px;
        }

        .user-name {
            font-weight: bold;
        }

        .user-job {
            font-style: italic;
            font-size: 10px;
            margin-top: 2px;
        }
    </style>
    <div class="card">
        <div class="card-image">
            <img class="user-image" src="user.png"/>
        </div>
        <div class="card-content">
            <div class="user-name"></div>
            <div class="user-job"></div>
        </div>    
    </div>`;

class UserCard extends HTMLElement {

    constructor() {
        super();
        this._shadowRoot = this.attachShadow({mode: 'closed'});
        this._shadowRoot.appendChild(template.content.cloneNode(true));
        this._name = this.getAttribute("name");
        this._job = this.getAttribute("job");
        this._image = this.getAttribute("image");
        this._shadowRoot.querySelector(".user-name").textContent = this._name;
        this._shadowRoot.querySelector(".user-job").textContent = this._job;
        this._shadowRoot.querySelector(".user-image").src = this._image;
    }
}

customElements.define("user-card", UserCard);
```

E temos como resultado o componente do cartão do usuário, podendo ser reutilizado em qualquer outra página HTML de nosso projeto:

![Resultado da página renderizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6riys3ir39hw43luwpw8.png)

## Conclusão

`Web Components` (componentes web) possui uma [especificação](https://www.w3.org/TR/2013/WD-components-intro-20130606/) própria. Como descrito no [MDN Web Docs](https://developer.mozilla.org/pt-BR/docs/Web/Web_Components), `Web Components` *é uma suíte de diferentes tecnologias que permite a criação de elementos customizados reutilizáveis — com a funcionalidade separada do resto do seu código — e que podem ser utilizados em suas aplicações web*.

Para utilizar `Web Components` não é necessário nenhuma biblioteca adicional ou *framework*, desde que o navegador implemente as seguintes especificações da Web Api:

- **Custom Elements** - permite definir tags customizadas
- **Templates** - permite definir blocos de códigos reutilizáveis
- **Shadow DOM** - permite encapsular o código do componente em uma árvore separada do DOM

Segundo a documentação, atualmente `Web Componentes` é suportado por padrão no Firefox (versão 63), Chrome, Opera e Edge (versão 79). O Safari já suporta boa parte delas mas não todas. De todo modo, é possível usar `Web Components` em qualquer navegador através do [Polyfill](https://github.com/webcomponents/polyfills/tree/master/packages/webcomponentsjs) - que nada mais é do que um pedaço de código (geralmente JavaScript) utilizado para simular os recursos ausentes do navegador o mais próximo possível.

`Web Components` é ainda um conceito novo quando utilizado em  JavaScript nativo. Componentes são muito utilizados por bibliotecas e *frameworks* como `Angular`, `React` e `Vue` - ferramentas sólidas e muito famosas dentro da comunidade front-end. E `Web Components`, por ser nativo, pode ser utilizado juntamente com essas ferramentas.

Se considerarmos uma equipe grande, separada em vários times, em que cada time utiliza uma ferramenta diferente para cada parte de um projeto, pode acontecer de existir partes em comum entre elas como uma tela de login - com a mesma estrutura para dar unidade ao projeto. Com `Web Components`, é possível criar um componente nativo que seja compartilhado entre os times. Ou seja, facilita a interoperabilidade do sistema.

Um artigo interessante que compara `Web Components` com demais ferramentas, levando em consideração estilos de código, performance e *bundle-size*, é o [All the Ways to Make a Web Component](https://webcomponents.dev/blog/all-the-ways-to-make-a-web-component/) do pessoal do [WebComponents.dev](https://webcomponents.dev/). Vale dar uma conferida!

No mais, a ideia deste post era apresentar conceitos básicos sobre `Web Components` e como construir um simples componente com pouco código. `Web Components` vai muito além. Em futuros  posts desta série pretendo mostrar outros recursos como o ciclo de vida de um componente, registro de eventos, componentes compostos e como podemos gerenciar melhor o estado de seus atributos. Até a próxima!