Ketika datang ke komponen web, mereka sering berkata: "Apa yang Anda inginkan tanpa kerangka kerja? Semuanya sudah siap di sana. " Bahkan, ada kerangka kerja yang dibuat berdasarkan implementasi dari standar yang termasuk dalam kelompok komponen web. Bahkan ada yang relatif bagus seperti
X-Tag . Tetapi hari ini, kita akan tetap memahami betapa sederhana, elegan dan kuatnya API browser untuk menyelesaikan tugas pengembangan sehari-hari, termasuk mengatur interaksi komponen satu sama lain dan dengan objek lain dari konteks eksekusi browser, dan Anda selalu dapat menggunakan kerangka kerja dengan komponen web, bahkan yang dikembangkan melintasi standar, termasuk melalui mekanisme yang akan kami analisis hari ini, setidaknya seperti yang dinyatakan
di situs .
Keputusan seperti reagen mengajarkan kepada kita bahwa harus ada satu sisi data besar dan komponen yang ditandatangani untuk perubahannya, dan dalam komponen web mereka juga mencoba untuk melihat tidak adanya subsistem seperti itu, pada kenyataannya, menyiratkan bahkan secara tidak sengaja membalikkan penandaan dan sudah ada dalam komponen web.
Setiap elemen memiliki atribut dari nilai yang dapat kita ubah. Dan jika Anda mencantumkan nama-nama dalam kait observAttributes, maka ketika mereka berubah,
atributChangedCallback () akan secara otomatis dipanggil di mana kita dapat menentukan perilaku komponen ketika atribut berubah. Menggunakan sihir pengambil, mudah untuk melakukan ikatan balik dengan cara yang sama.
Kami sudah membuat sketsa beberapa proyek di bagian
pertama dan hari ini kami akan terus memotongnya lebih lanjut.
Ada baiknya segera menyebutkan satu batasan, setidaknya dalam implementasi saat ini, bahwa nilai atribut hanya bisa menjadi nilai primitif yang ditentukan oleh literal dan dapat direduksi menjadi string, tetapi secara umum dimungkinkan untuk mentransfer "objecs" dengan cara ini menggunakan tanda kutip tunggal dari luar dan tanda kutip ganda untuk menentukan nilai dan bidang proyek.
<my-component my='{"foo": "bar"}'></my-component>
Untuk menggunakan nilai ini dalam kode, Anda bisa menerapkan pengambil
sihir otomatis yang akan memanggil
JSON.parse () di atasnya .
Tetapi untuk sekarang, nilai numerik penghitung sudah cukup bagi kami.
Kami menambahkan atribut baru ke elemen kami, menetapkannya seperti yang diamati, klik penangan klik untuk menambah penghitung ini, dan menambahkan pembaruan nilai yang ditampilkan dengan tautan langsung ke kait perubahan, logika yang diimplementasikan dalam metode
updateLabel () yang dapat digunakan kembali secara terpisah.
export class MyWebComp extends HTMLElement { constructor() { super(); } connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(html); this.updateLabel(); } updateLabel() { this.shadowRoot.querySelector('#helloLabel').textContent = 'Hello ' + this.getAttribute('greet-name') + ' ' + this.getAttribute('count'); } static get observedAttributes() { return ['count']; } attributeChangedCallback(name, oldValue, newValue) { if (name === 'count') { this.updateLabel(); } } showMessage(event) { this.setAttribute('count', this.getAttribute('count') + 1); } }

Setiap item menerima penghitung independen yang diperbarui secara otomatis.
Pekerjaan rumah: menerapkan konversi penghitung ke nomor dan digunakan melalui pengambil otomatis;)
Tentu saja, opsi yang lebih maju dimungkinkan. Misalnya, jika Anda tidak mempertimbangkan panggilan balik dan acara sederhana, menggunakan Proxy asli dan getter dan setter yang sama, Anda dapat menerapkan fungsi mengikat terbalik.
Tambahkan file
my-counter.js dengan kelas semacam ini
export class MyCounter extends EventTarget { constructor() { super(); this.count = 0; } increment() { this.count++; this.dispatchEvent(new CustomEvent('countChanged', { detail: { count: this.count } })); } }
Kami mewarisi kelas dari
EventTarget sehingga kelas lain dapat berlangganan acara yang dilemparkan oleh objek dari kelas ini dan mendefinisikan properti hitung yang akan menyimpan nilai penghitung.
Sekarang tambahkan instance dari kelas ini sebagai properti statis untuk komponen.
<script type="module"> import { MyWebComp } from "./my-webcomp.js"; import { MyCounter } from "./my-counter.js"; let counter = new MyCounter(); Object.defineProperty(MyWebComp.prototype, 'counter', { value: counter }); customElements.define('my-webcomp', MyWebComp); </script>
Dan dalam kode komponen, kami akan berlangganan perubahan nilai metode pembaruan label
updateLabel () , yang kami tambahkan tampilan nilai dari penghitung bersama global. Dan di handler klik, panggilan langsung ke metode tambahan.
export class MyWebComp extends HTMLElement { constructor() { super(); } connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(html); this.updateLabel(); this.counter.addEventListener('countChanged', this.updateLabel.bind(this)); } updateLabel() { this.shadowRoot.querySelector('#helloLabel').textContent = 'Hello ' + this.getAttribute('greet-name') + ' ' + this.getAttribute('count') + ' ' + this.counter.count; } static get observedAttributes() { return ['count']; } attributeChangedCallback(name, oldValue, newValue) { if (name === 'count') { if (this.shadowRoot) { this.updateLabel(); } } } showMessage(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); this.counter.increment(); } }
Terlepas dari kenyataan bahwa setiap elemen menerima dua penghitung, masing-masing nilainya akan bertambah secara independen, tetapi nilai penghitung bersama akan sama untuk kedua elemen, dan nilai-nilai individual akan ditentukan oleh jumlah klik hanya pada mereka.

Dengan demikian, kami mendapat pengikatan langsung dalam pengikatan langsung, dan karena penggunaan acara, nilai ini lemah dalam pembaruan. Tentu saja, tidak ada yang mencegah kenaikan dari diterapkan melalui acara, dengan mengikat metode
kenaikan () ke lyser acara:
export class MyCounter extends EventTarget { constructor() { super(); this.count = 0; this.addEventListener('increment', this.increment.bind(this)); } increment() { this.count++; this.dispatchEvent(new CustomEvent('countChanged', { detail: { count: this.count } })); } }
dan mengganti pemanggilan metode dengan lemparan acara:
export class MyWebComp extends HTMLElement { ... showMessage(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); this.counter.dispatchEvent(new CustomEvent('increment')); } }
Apa yang berubah? Sekarang, jika selama pengembangan metode
increment () dihapus atau diubah, kebenaran kode kita akan dilanggar, tetapi tidak akan ada kesalahan juru bahasa, mis. operabilitas akan tetap. Karakteristik ini disebut konektivitas lemah.
Dalam pengembangan, baik pengikatan langsung maupun lemah diperlukan, biasanya merupakan kebiasaan untuk menerapkan pengikatan langsung di dalam logika satu modul komponen, dan lemahnya sambungan antara berbagai modul dan komponen untuk memberikan fleksibilitas dan ekstensibilitas dari keseluruhan sistem. Semantik HTML menyiratkan tingkat fleksibilitas yang tinggi, mis. preferensi untuk konektivitas yang lemah, tetapi aplikasi akan bekerja lebih andal jika koneksi di dalam komponen langsung.
Kita dapat menutup telepon dengan cara lama dan memanggil acara di objek
dokumen global.
export class MyWebComp extends HTMLElement { constructor() { super(); } connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(html); this.updateLabel(); this.counter.addEventListener('countChanged', this.updateLabel.bind(this)); document.addEventListener('countChanged', this.updateLabel.bind(this)); } disconnectedCallback() { document.removeEventListener(new CustomEvent('increment')); document.removeEventListener(new CustomEvent('countChanged')); } ... showMessage(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); this.counter.dispatchEvent(new CustomEvent('increment')); document.dispatchEvent(new CustomEvent('increment')); } }
export class MyCounter extends EventTarget { constructor() { super(); this.count = 0; this.addEventListener('increment', this.increment.bind(this)); document.addEventListener('increment', this.increment.bind(this)); } increment() { this.count++; this.dispatchEvent(new CustomEvent('countChanged', { detail: { count: this.count } })); document.dispatchEvent(new CustomEvent('countChanged', { detail: { count: this.count } })); } }
Perilaku tidak berbeda, tetapi sekarang kita perlu berpikir tentang menghapus event handler dari objek global jika elemen itu sendiri dihapus dari pohon. Untungnya, komponen web memberi kita tidak hanya kait desain, tetapi juga penghancuran, menghindari kebocoran memori.
Ada juga satu poin lebih penting: peristiwa elemen pohon dapat berinteraksi satu sama lain melalui mekanisme yang disebut "menggelegak".
Ketika pengguna mengklik elemen kami, acara, setelah bekerja pada elemen itu sendiri, mulai dipanggil pada induk seperti lingkaran di atas air, kemudian pada induk dari induk ini dan seterusnya ke elemen root.
Kapan saja dalam rantai panggilan ini, acara tersebut dapat dicegat dan diproses.
Ini, tentu saja, bukan peristiwa yang sama persis, dan turunan dan konteksnya, meskipun mereka akan mengandung tautan satu sama lain seperti misalnya dalam
event.path , tidak akan sepenuhnya bertepatan.
Namun, mekanisme ini memungkinkan Anda untuk mengaitkan elemen anak dengan orang tua mereka sedemikian rupa sehingga tautan ini tidak melanggar pola teknik, mis. tanpa tautan langsung, tetapi hanya karena perilaku mereka sendiri.
Di zaman kuno, ini juga menimbulkan banyak masalah, ketika peristiwa beberapa elemen menyebabkan "lingkaran" atau gema di beberapa bagian dokumen yang tidak diinginkan untuk reaksi.
Selama bertahun-tahun, pengembang web berjuang dengan "propaganda" (menyebarkan atau memunculkan) peristiwa dan menghentikannya dan mengubahnya semampu mereka, tetapi untuk penggunaan yang nyaman, seperti dalam kasus dengan ID, hanya isolasi pohon bayangan yang kurang.
Keajaiban di sini adalah bahwa peristiwa tidak menjarah di luar Shadow Tree. Namun demikian, Anda dapat menangkap peristiwa yang dilemparkan oleh anak-anaknya di komponen host dan menggulungnya di pohon dalam bentuk beberapa peristiwa lain yang berarti, baik, atau hanya memprosesnya.
Untuk memahami cara kerjanya, kami membungkus elemen
webcomp kami dalam wadah seperti ini:
<my-webcont count=0> <my-webcomp id="myWebComp" greet-name="John" count=0 onclick="this.showMessage(event)"></my-webcomp> <my-webcomp id="myWebComp2" greet-name="Josh" count=0 onclick="this.showMessage(event)"></my-webcomp> </my-webcont>
Wadah akan memiliki kode ini:
export class MyWebCont extends HTMLElement { constructor() { super(); } connectedCallback() { this.addEventListener('increment', this.updateCount.bind(this)); } updateCount(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); } static get observedAttributes() { return ['count']; } attributeChangedCallback(name, oldValue, newValue) { if (name === 'count') { this.querySelectorAll('my-webcomp').forEach((el) => { el.setAttribute('count', newValue); }); } } }
Di
terhubungCallback (), kami akan menggantung pawang pada acara
kenaikan , yang akan melempar anak-anak. Pawang akan menambah penghitung elemen sendiri, dan panggilan balik untuk mengubah nilainya akan melewati semua elemen anak dan menambah penghitung mereka, tempat pawang yang sebelumnya dikembangkan oleh kami digantung.
Kode elemen anak akan berubah sedikit, pada kenyataannya, yang kita butuhkan adalah untuk acara
increment untuk membuang elemen itu sendiri dan bukan agregatnya, dan melakukannya dengan
gelembung: atribut
benar .
export class MyWebComp extends HTMLElement { ... showMessage(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); this.counter.dispatchEvent(new CustomEvent('increment')); this.dispatchEvent(new CustomEvent('increment', { bubbles: true })); document.dispatchEvent(new CustomEvent('increment')); } }

Sekarang, meskipun ada keacakan umum, penghitung elemen pertama akan selalu menunjukkan nilai induknya. Secara keseluruhan, apa yang telah kami lakukan di sini hari ini tentu saja bukan contoh untuk diikuti, tetapi hanya gambaran tentang kemungkinan dan teknik yang dengannya Anda dapat mengatur interaksi modular yang benar-benar antar komponen, menghindari minimum alat.
Anda dapat menemukan kode yang sudah jadi untuk referensi di repositori yang sama, di
acara brunch .