Komponen web tanpa Shadow DOM

Halo, Habr!

Baru-baru ini, saya memperhatikan sejumlah artikel yang mengkritik semua jenis komponen web. Terkadang, kritik ini sangat keras dan bahkan menampar kebencian. Menurut pendapat saya, masalah utama di sini adalah kurangnya praktik yang mapan dalam bekerja dengan kelompok standar ini di antara komunitas pengembang. Banyak model yang akrab tidak selalu cocok secara organik ke dalam proyek yang melibatkan Elemen Kustom dan Shadow DOM , banyak hal harus dilihat dari sudut pandang baru, dan tidak semua orang menyukainya. Saya telah berhasil bekerja dengan komponen web selama beberapa tahun dan bahkan mengembangkan perpustakaan saya sendiriberdasarkan pada mereka, jadi saya pikir situasi ini tidak terlalu adil. Saya akan mencoba, setidaknya sebagian, untuk memperbaikinya, sejauh kekuatan saya yang sederhana. Saya memutuskan untuk membuat serangkaian publikasi yang ringkas, di mana masing-masing saya berencana untuk menyentuh salah satu aspek yang sering dikritik, serta untuk menunjukkan sejumlah teknik teknis yang mungkin menarik bagi mereka yang belum memutuskan di mana ia harus menghadapi barikade. Hari ini saya ingin berbicara tentang cara membuat komponen tanpa DOM Bayangan.

Untuk apa?


Ide utama yang ingin saya sampaikan kali ini adalah bahwa komponen web dan Shadow DOM bukanlah hal yang sama. Saat menggunakan Shadow DOM, Anda mendapatkan dua manfaat utama:

  • Bagian terpisah dari dokumen di mana gaya Anda merasa aman dari pengaruh eksternal dan "kebocoran"
  • Mekanisme komposisi yang memungkinkan Anda untuk membagi dokumen menjadi apa struktur komponen itu sendiri dan isinya (turunan dari elemen DOM di pohon)

Namun, mekanik ini juga menanggung biaya untuk menciptakan dan menata ruang yang terisolasi, yang cukup alami. Dalam beberapa kasus (daftar besar, sel tabel dengan data, dll.), Saya ingin menghindari biaya ini untuk alasan optimasi kinerja. Sekarang kita akan memperbaikinya:

const MY_CSS = {
  title: 'color: #00f; font-size: 2em',
  item: 'color: #f00; font-size: 1.2em',
};

const DATA = [
  {text: 'Text 1'},
  {text: 'Text 2'},
  {text: 'Text 3'},
];

let template = document.createElement('template');
template.innerHTML = /*html*/ `
<div style="${MY_CSS_.title}">List items:</div>
<div class="my-list">
  ${DATA.map(item => /*html*/ `<div style="${MY_CSS.item}">${item.text}</div>`).join('')}
</div>
`;

class ShadowlessComponent extends HTMLElement {
  constructor() {
    super();
    this._contents = new DocumentFragment();
    this._contents.appendChild(template.content.cloneNode(true));
  }
  connectedCallback() {
    this.appendChild(this._contents);
  }
}

window.customElements.define('shadowless-component', ShadowlessComponent);

Jika Anda sudah terbiasa dengan standar Elemen Kustom, Anda akan segera melihat apa yang terjadi: alih-alih memanggil metode attachShadowdalam konstruktor komponen, kami membuat DocumentFragment di mana kami mengkloning templat yang sudah disiapkan sebelumnya. Pada tahap ini, komponen tidak dirender oleh browser dan dapat dimodifikasi secara relatif aman, misalnya, mengikat / memasukkan data.

Langkah penting berikutnya terkait dengan siklus hidup Elemen Kustom. Komponen ditambahkan ke dokumen umum hanya setelah konstruktor telah berfungsi penuh dan sampai saat itu, bagian dari DOM API yang bertanggung jawab untuk bekerja dengan orang tua atau keturunan elemen, serta dengan atribut, tidak akan tersedia. Karenanya, untuk langsung menambahkan konten ke komponen kami, kami menggunakan connectedCallback.

Saat membuat templat, kami menggunakan metode untuk kesederhanaan innerHTML. Operasi ini dilakukan hanya sekali, ketika membuat elemen "template", itu tidak diulang setiap kali komponen dari komponen kami dibuat. Namun, titik ini juga dapat lebih dioptimalkan dengan menciptakan pola secara imperatif.

Secara total, menggunakan tag khusus di markup kami shadowless-component, kami mendapatkan hasil berikut di browser:

<shadowless-component>
  <div id="caption" style="color: #00f; font-size: 2em">List items:</div>
    <div class="my-list">
      <div style="color: #f00; font-size: 1.2em">Text 1</div>
      <div style="color: #f00; font-size: 1.2em">Text 2</div>
      <div style="color: #f00; font-size: 1.2em">Text 3</div>
    </div>
</shadowless-component>

Karena, setelah menyingkirkan ShadowRoot, kami kehilangan isolasi gaya, kami menambahkan gaya ke templat kami menggunakan atribut. Dalam hal ini, mereka memiliki prioritas, ini menyelesaikan sebagian masalah dan dapat digunakan di tempat-tempat penting. Untuk semua kasus lain, gaya klasik tersedia melalui lembar gaya umum, dan tag khusus berfungsi sebagai pemilih yang nyaman.

display: isi


Komponen web adalah simpul penuh DOM Anda. Ini berarti, selain fakta bahwa semua metode standar elemen DOM tersedia untuk Anda, dan komponen Anda selalu berupa wadah. Artinya, jika Anda ingin menambahkan struktur unsur-unsur ke DOM menggunakan komponen web, mereka semua akan menjadi turunan dari komponen Anda, yang tidak selalu nyaman. Dalam kasus seperti itu, Anda dapat menggunakan aturan CSS baru - display: content . Dukungan browser: caniuse.com/#feat=css-display-contents

Secara default, semua komponen memiliki tampilan: properti inline .

Sedikit kerusakan


Tetapi bagaimana jika kita sama sekali tidak menginginkan wadah tambahan dan tag khusus? Berikan HTML murni!

BAIK:

  constructor() {
    super();
    this._contents = new DocumentFragment();
    this._contents.appendChild(template.content.cloneNode(true));
    this._titleEl = this._contents.querySelector('#caption');
    window.setInterval(() => {
      this._titleEl.textContent = Date.now();
    }, 1000);
  }
  connectedCallback() {
    this.parentNode.prepend(this._contents, this);
    this.remove();
  }

Sebagai hasilnya, kami mendapatkan ini:

<div id="caption" style="color: #00f; font-size: 2em">1581075598392</div>
<div class="my-list">
  <div style="color: #f00; font-size: 1.2em">Text 1</div>
  <div style="color: #f00; font-size: 1.2em">Text 2</div>
  <div style="color: #f00; font-size: 1.2em">Text 3</div>
</div>

Semua acara dan binding terus bekerja dan dikendalikan oleh komponen kami, yang sekarang hanya ada di memori. Dalam hal ini, Anda harus berhati-hati dalam menghentikan langganan dan pembersihan lainnya dari sampah saat Anda ingin menghapus komponen sepenuhnya.

Komponen CSS


Menurut standar, tag khusus harus dinamai dengan penambahan karakter "-" wajib. Jika Anda menggunakan tag Anda di markup, tetapi pada saat yang sama tidak membuat komponen apa pun di JS dan menambahkan konstruktornya ke registri komponen, browser menganggap tag Anda sebagai "elemen tidak dikenal" ( HTMLUnknownElement ). Secara default, elemen-elemen ini memiliki perilaku yang mirip dengan tag span. Ini dapat digunakan jika Anda perlu membuat komponen bodoh sederhana dengan struktur sederhana yang aturan CSS :: before , :: after dan attr () cukup . Contoh:

  my-container {
    display: block;
    padding: 10px;
    border: 1px solid currentColor;
  }
  my-container::before {
    content: attr(caption);
    margin-bottom: .6em;
  }

Gunakan di markup:

<my-container caption=""></my-container>

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


All Articles