Web Components são mais fáceis do que você pensa

Quando ia a conferências e via apresentações sobre o tema de componentes web, sempre achei que não era apenas elegante, mas também bastante difícil. Mil linhas de JavaScript para armazenar apenas 4 linhas de HTML. O palestrante inevitavelmente escondeu uma grande quantidade de código JS atrás de coisas simples ou mergulhou em detalhes complexos, então meus olhos começaram a se fechar de tédio e comecei a pensar se minha renda diária cobria o custo dos lanches.





No entanto, em um projeto recente feito para aprender HTML facilmente (é claro, adicionando zumbis e piadas bobas), decidi que era necessário descrever cada elemento HTML na especificação. Além dessa conferência, comecei com a introdução <slot>



e <template>



os elementos, e quando quis escrever algo interessante sobre eles no projeto, tive que me aprofundar no assunto.





E à medida que fui mais fundo, percebi que Web Components são mais fáceis do que eu pensava.





Ou os componentes da web percorreram um longo caminho desde que sonhei em fazer um lanche em uma conferência ou deixei meu medo inicial atrapalhar o fato de realmente conhecê-los, ou talvez ambos.





Estou aqui para dizer que sim, você pode criar um componente da web. Vamos colocar o medo e até lanches do lado de fora para juntar tudo.





Vamos começar com um <template>

<template>



é um elemento HTML que nos permite criar um template (estrutura HTML para componentes web).





O código
<template>
  <p>The Zombies are coming!</p>
</template>
      
      



O elemento <template>



é muito importante porque permite manter tudo junto. É como uma base para a sua casa, uma base a partir da qual tudo o que chamamos de edifício acabado começa a ser construído. Vamos usar este pequeno trecho de código para nosso <apocalyptic-warning>



componente que nos notifica sobre o apocalipse zumbi que se aproxima.





Depois, há o componente <slot>

<slot>



é apenas um elemento HTML, assim como <template>



. Mas, no nosso caso, ele <slot>



configura o que <template>



exibe na página.





O código
<template>
  <p>The <slot>Zombies</slot> are coming!</p>
</template>
      
      



"Zombies" ( ?) <template>



. , . "Zombies".





<slot>



placeholder



. placeholder



, , - placeholder



. - name



.





<template>
  <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>
      
      



name



- , <template>



. "whats-coming" . , -, <slot>



, - , , .





-

, , ( : JS , ).





<apocalyptic-warning>
  <span slot="whats-coming">Halitosis Laden Undead Minions</span>
</apocalyptic-warning>

<template>
  <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>
      
      



, ? <apocalyptic-warning>



, HTML . <span>



"whats-coming". <span>



"Zombies", .





, - , HTML , . , , -.





? , ? , . , <slot>



, JavaScript.





, JavaScript , , , , . , , .





-, -. : , .





, .





//   -   

customElements.define("apocalyptic-warning", class extends HTMLElement {
  	  //  ,          HTML 
  
  	//   ,    
    constructor() {

      //   , .   HTMLElement.      HTML .
      super();

      //  <template>      `warinng`
      let warning = document.getElementById("warningtemplate");
      
      //      `mywarning`
      let mywarning = warning.content;

      const shadowRoot = this.attachShadow({mode: "open"}).appendChild(mywarning.cloneNode(true));
    }
});
      
      



, .





const shadowRoot = this.attachShadow({mode: "open"}).appendChild(mywarning.cloneNode(true));
      
      



. -, - (this) , Shadow DOM.{ mode: open }



, JavaScript :root



Shadow DOM , - . Shadow DOM ( : HTML Node). , . , Shadow DOM , <slot>



slot , .





. -, , .





JS:





customElements.define('apocalyptic-warning',
    class extends HTMLElement {
      constructor() {
        super();
        let warning = document.getElementById('warningtemplate');
        let mywarning = warning.content;
        
         const shadowRoot = this.attachShadow({mode: 'open'}).appendChild(mywarning.cloneNode(true));
        
      }
    });
      
      



HTML:





<p>The Apocalypse will never happen!</p>
<apocalyptic-warning>
   <span slot="whats-coming">Undead</span>
</apocalyptic-warning>
<apocalyptic-warning>
   <span slot="whats-coming">Halitosis Laden Zombie Minions</span>
</apocalyptic-warning>
<template id="warningtemplate">
  <style>
    p {
      background-color: pink;
      padding: 0.5em;
      border: 1px solid red;
    }
  </style>
    <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>
      
      



: Codepen





, . , CSS. , <style>



<template>



.





<template id="warningtemplate">
  <style>
    p {
      background-color: pink;
      padding: 0.5em;
      border: 1px solid red;
    }
  </style>

    <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>
      
      



, , Shadow DOM.





, , , , , Shadow DOM. , Shadow DOM DOM , . - , DOM .





, , <style>,



<slot>



. , . - , CSS , , . , -, CSS.





apocalyptic-warning span {
  color: blue;
}
      
      



, CSS <template>



.





JavaScript , , , , <zombie-profile>.







customElements.define("zombie-profile",
  class extends HTMLElement {
    constructor() {
      super();
      let profile = document.getElementById("zprofiletemplate");
      let myprofile = profile.content;
      const shadowRoot = this.attachShadow({mode: "open"}).appendChild(myprofile.cloneNode(true));
    }
  }
);
      
      



HTML CSS.





<template id="zprofiletemplate">
  <style>
    img {
      width: 100%;
      max-width: 300px;
      height: auto;
      margin: 0 1em 0 0;
    }
    h2 {
      font-size: 3em;
      margin: 0 0 0.25em 0;
      line-height: 0.8;
    }
    h3 {
      margin: 0.5em 0 0 0;
      font-weight: normal;
    }
    .age, .infection-date {
      display: block;
    }
    span {
      line-height: 1.4;
    }
    .label {
      color: #555;
    }
    li, ul {
      display: inline;
      padding: 0;
    }
    li::after {
      content: ', ';
    }
    li:last-child::after {
      content: '';
    }
    li:last-child::before {
      content: ' and ';
    }
  </style>

  <div class="profilepic">
    <slot name="profile-image"><img src="https://assets.codepen.io/1804713/default.png" alt=""></slot>
  </div>

  <div class="info">
    <h2><slot name="zombie-name" part="zname">Zombie Bob</slot></h2>

    <span class="age"><span class="label">Age:</span> <slot name="z-age">37</slot></span>
    <span class="infection-date"><span class="label">Infection Date:</span> <slot name="idate">September 12, 2025</slot></span>

    <div class="interests">
      <span class="label">Interests: </span>
      <slot name="z-interests">
        <ul>
          <li>Long Walks on Beach</li>
          <li>brains</li>
          <li>defeating humanity</li>
        </ul>
      </slot>
    </div>

    <span class="z-statement"><span class="label">Apocalyptic Statement: </span> <slot name="statement">Moooooooan!</slot></span>

  </div>
</template>
      
      



CSS <zombie-profile>



CSS. , , , <template>



.





zombie-profile {
  width: calc(50% - 1em);
  border: 1px solid red;
  padding: 1em;
  margin-bottom: 2em;
  display: grid;
  grid-template-columns: 2fr 4fr;
  column-gap: 20px;
}
zombie-profile img {
  width: 100%;
  max-width: 300px;
  height: auto;
  margin: 0 1em 0 0;
}
zombie-profile li, zombie-profile ul {
  display: inline;
  padding: 0;
}
zombie-profile li::after {
  content: ', ';
}
zombie-profile li:last-child::after {
  content: '';
}
zombie-profile li:last-child::before {
  content: ' and ';
}
      
      



:





: Codepen





, , , -, . , , - , , .





Isso é tudo. Do que você tem mais medo agora: componentes da web ou um apocalipse zumbi? Em um passado não tão distante, eu poderia ter dito que os componentes da web, mas agora os zumbis são a única coisa que me preocupa (bem, e minha mesada diária cobrirá os custos dos lanches).








All Articles