1. Pengantar
Terakhir diperbarui: 10-08-2021
Komponen Web
Komponen Web adalah sekumpulan API platform web yang memungkinkan Anda membuat tag HTML khusus, yang dapat digunakan kembali, dan terenkapsulasi untuk digunakan di halaman web dan aplikasi web. Widget dan komponen khusus yang dibuat berdasarkan standar Komponen Web akan berfungsi di seluruh browser modern dan dapat digunakan dengan pustaka atau framework JavaScript apa pun yang bekerja dengan HTML.
Apa yang dimaksud dengan Lit
Lit adalah library sederhana untuk membuat komponen web yang cepat dan ringan yang dapat berfungsi di framework apa pun, atau tanpa framework sama sekali. Dengan Lit, Anda dapat membuat komponen, aplikasi, sistem desain yang dapat dibagikan, dan banyak lagi.
Lit menyediakan API untuk menyederhanakan tugas Komponen Web umum seperti mengelola properti, atribut, dan rendering.
Yang akan Anda pelajari
- Apa itu Komponen Web
- Konsep Komponen Web
- Cara membuat Komponen Web
- Apa yang dimaksud dengan lit-html dan LitElement
- Yang dilakukan Lit selain komponen web
Yang akan Anda bangun
- Komponen Web vanila yang disukai / tidak disukai
- Komponen Web Berbasis Lit yang disukai
Yang Anda butuhkan
- Semua browser modern yang diupdate (Chrome, Safari, Firefox, Chromium Edge). Komponen Web yang berfungsi di semua browser modern dan polyfill tersedia untuk Microsoft Internet Explorer 11 dan Microsoft Edge non-chromium.
- Pengetahuan terkait HTML, CSS, JavaScript, dan Chrome DevTools.
2. Mempersiapkan & menjelajahi Playground
Mengakses kode
Di sepanjang codelab ini, akan ada link ke playground Lit seperti ini:
Playground adalah sandbox kode yang berjalan sepenuhnya di browser Anda. Library ini dapat mengompilasi dan menjalankan file TypeScript dan JavaScript, dan juga dapat menyelesaikan impor ke modul node secara otomatis. mis.
// before
import './my-file.js';
import 'lit';
// after
import './my-file.js';
import 'https://unpkg.com/lit?module';
Anda dapat melakukan seluruh tutorial di playground Lit, menggunakan checkpoint ini sebagai titik awal. Jika menggunakan VS Code, Anda bisa menggunakan checkpoint ini untuk mendownload kode awal pada langkah apa pun, serta menggunakannya untuk memeriksa pekerjaan Anda.
Menjelajahi UI playground yang menyala
Screenshot UI playground Lit menyoroti bagian yang akan Anda gunakan dalam codelab ini.
- Pemilih file. Perhatikan tombol plus...
- Editor file.
- Pratinjau kode.
- Tombol Muat ulang.
- Tombol Download.
Penyiapan VS Code (Lanjutan)
Berikut manfaat menggunakan penyiapan VS Code:
- Pemeriksaan jenis template
- Template Intel & pelengkapan otomatis
Jika Anda sudah menginstal NPM, VS Code (dengan plugin lit-plugin) dan mengetahui cara menggunakan lingkungan tersebut, Anda dapat mendownload dan memulai project ini dengan melakukan hal berikut:
- Tekan tombol download
- Ekstrak konten file tar ke direktori
- Instal server dev yang dapat menyelesaikan penentu modul kosong (tim Lit merekomendasikan @web/dev-server)
- Berikut adalah contoh
package.json
- Berikut adalah contoh
- Jalankan server dev dan buka browser (jika menggunakan
@web/dev-server
, Anda dapat menggunakannpx web-dev-server --node-resolve --watch --open
)- Jika Anda menggunakan contoh
package.json
, gunakannpm run serve
- Jika Anda menggunakan contoh
3. Tentukan Elemen Khusus
Elemen Kustom
Komponen Web adalah kumpulan 4 API web native. Bagian-bagian tersebut adalah:
- Modul ES
- Elemen Kustom
- DOM Bayangan
- Template HTML
Anda sudah menggunakan spesifikasi modul ES, yang memungkinkan Anda membuat modul JavaScript dengan impor dan ekspor yang dimuat ke halaman dengan <script type="module">
.
Menentukan Elemen Kustom
Spesifikasi Elemen Kustom memungkinkan pengguna menentukan elemen HTML mereka sendiri menggunakan JavaScript. Nama harus berisi tanda hubung (-
) untuk membedakannya dari elemen browser native. Kosongkan file index.js
dan tentukan class elemen kustom:
index.js
class RatingElement extends HTMLElement {}
customElements.define('rating-element', RatingElement);
Elemen kustom ditentukan dengan mengaitkan class yang memperluas HTMLElement
dengan nama tag dengan tanda hubung. Panggilan ke customElements.define
memberi tahu browser untuk mengaitkan class RatingElement
dengan tagName ‘rating-element'
. Artinya, setiap elemen dalam dokumen Anda dengan nama <rating-element>
akan dikaitkan dengan class ini.
Tempatkan <rating-element>
dalam isi dokumen dan lihat apa yang dirender.
index.html
<body>
<rating-element></rating-element>
</body>
Sekarang, dengan melihat output-nya, Anda akan melihat bahwa tidak ada yang dirender. Ini wajar terjadi karena Anda belum memberi tahu browser cara merender <rating-element>
. Anda dapat mengonfirmasi bahwa definisi Elemen Kustom telah berhasil dengan memilih <rating-element>
di Chrome Dev Tools pemilih elemen dan, di konsol, memanggil:
$0.constructor
Yang akan menghasilkan output:
class RatingElement extends HTMLElement {}
Siklus Proses Elemen Kustom
Elemen Kustom dilengkapi dengan serangkaian hook siklus proses. Bagian-bagian tersebut adalah:
constructor
connectedCallback
disconnectedCallback
attributeChangedCallback
adoptedCallback
constructor
dipanggil saat elemen pertama kali dibuat: misalnya, dengan memanggil document.createElement(‘rating-element')
atau new RatingElement()
. Konstruktor adalah tempat yang baik untuk menyiapkan elemen Anda, tetapi biasanya dianggap praktik yang buruk untuk melakukan manipulasi DOM dalam konstruktor untuk elemen "boot-up" alasan performa.
connectedCallback
dipanggil saat elemen kustom dilampirkan ke DOM. Di sinilah manipulasi DOM awal terjadi.
disconnectedCallback
dipanggil setelah elemen kustom dihapus dari DOM.
attributeChangedCallback(attrName, oldValue, newValue)
dipanggil saat salah satu atribut yang ditentukan pengguna berubah.
adoptedCallback
dipanggil saat elemen kustom diadopsi dari documentFragment
lain ke dalam dokumen utama melalui adoptNode
seperti di HTMLTemplateElement
.
Render DOM
Sekarang, kembali ke elemen kustom dan kaitkan beberapa DOM dengannya. Setel konten elemen saat dilampirkan ke DOM:
index.js
class RatingElement extends HTMLElement {
constructor() {
super();
this.rating = 0;
}
connectedCallback() {
this.innerHTML = `
<style>
rating-element {
display: inline-flex;
align-items: center;
}
rating-element button {
background: transparent;
border: none;
cursor: pointer;
}
</style>
<button class="thumb_down" >
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>
`;
}
}
customElements.define('rating-element', RatingElement);
Di constructor
, Anda menyimpan properti instance yang disebut rating
di elemen. Di connectedCallback
, Anda menambahkan turunan DOM ke <rating-element>
untuk menampilkan rating saat ini, beserta tombol suka dan tidak suka.
4. DOM Bayangan
Mengapa Shadow DOM?
Pada langkah sebelumnya, Anda akan melihat bahwa pemilih di tag gaya yang Anda sisipkan untuk memilih elemen rating pada halaman serta tombol apa pun. Hal ini dapat menyebabkan gaya bocor dari elemen dan memilih simpul lain yang mungkin tidak ingin Anda tata gayanya. Selain itu, gaya lain di luar elemen kustom ini mungkin tidak sengaja menata gaya node di dalam elemen kustom Anda. Misalnya, coba tempatkan tag gaya di bagian head dokumen utama:
index.html
<!DOCTYPE html>
<html>
<head>
<script src="./index.js" type="module"></script>
<style>
span {
border: 1px solid red;
}
</style>
</head>
<body>
<rating-element></rating-element>
</body>
</html>
Output Anda akan memiliki kotak batas merah di sekitar rentang rating. Ini adalah kasus yang sepele, namun kurangnya enkapsulasi DOM dapat mengakibatkan masalah yang lebih besar untuk aplikasi yang lebih kompleks. Di sinilah Shadow DOM berperan.
Memasang Root Bayangan
Lampirkan Root Bayangan ke elemen dan render DOM di dalam root tersebut:
index.js
class RatingElement extends HTMLElement {
constructor() {
super();
this.rating = 0;
}
connectedCallback() {
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
</style>
<button class="thumb_down" >
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>
`;
}
}
customElements.define('rating-element', RatingElement);
Saat memuat ulang halaman, Anda akan melihat bahwa gaya dalam dokumen utama tidak lagi dapat memilih node di dalam Shadow Root.
Bagaimana Anda melakukannya? Dalam connectedCallback
, Anda memanggil this.attachShadow
yang melampirkan root bayangan ke elemen. Mode open
berarti konten bayangan dapat diperiksa dan juga membuat root bayangan dapat diakses melalui this.shadowRoot
. Lihat juga Komponen Web di pemeriksa Chrome:
Sekarang Anda akan melihat akar bayangan yang dapat diperluas yang menampung konten. Segala sesuatu di dalam shadow root itu disebut Shadow DOM. Jika Anda memilih elemen rating di Chrome Dev Tools dan memanggil $0.children
, Anda akan melihat bahwa elemen tersebut tidak menampilkan turunan. Ini karena Shadow DOM tidak dianggap sebagai bagian dari hierarki DOM yang sama dengan turunan langsung, melainkan Shadow Tree.
DOM Ringan
Eksperimen: menambahkan node sebagai turunan langsung dari <rating-element>
:
index.html
<rating-element>
<div>
This is the light DOM!
</div>
</rating-element>
Muat ulang halaman, dan Anda akan melihat bahwa node DOM baru di Light DOM Elemen Kustom ini tidak muncul di halaman. Hal ini karena Shadow DOM memiliki fitur untuk mengontrol cara node Light DOM diproyeksikan ke dalam shadow dom melalui elemen <slot>
.
5. Template HTML
Mengapa Template
Menggunakan innerHTML
dan string literal template tanpa sanitasi dapat menyebabkan masalah keamanan dengan injeksi skrip. Metode di masa lalu telah mencakup penggunaan DocumentFragment
, tetapi masalah ini juga disertai dengan masalah lain seperti pemuatan gambar dan skrip yang berjalan saat template ditentukan serta menghadirkan hambatan untuk digunakan kembali. Di sinilah elemen <template>
berperan; template menyediakan DOM inert, metode berperforma tinggi untuk meng-clone node, dan template yang dapat digunakan kembali.
Menggunakan Template
Selanjutnya, transisikan komponen untuk menggunakan Template HTML:
index.html
<body>
<template id="rating-element-template">
<style>
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
</style>
<button class="thumb_down" >
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating"></span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>
</template>
<rating-element>
<div>
This is the light DOM!
</div>
</rating-element>
</body>
Di sini Anda telah memindahkan konten DOM ke tag template di DOM dokumen utama. Sekarang faktorkan ulang definisi elemen kustom:
index.js
class RatingElement extends HTMLElement {
constructor() {
super();
this.rating = 0;
}
connectedCallback() {
const shadowRoot = this.attachShadow({mode: 'open'});
const templateContent = document.getElementById('rating-element-template').content;
const clonedContent = templateContent.cloneNode(true);
shadowRoot.appendChild(clonedContent);
this.shadowRoot.querySelector('.rating').innerText = this.rating;
}
}
customElements.define('rating-element', RatingElement);
Untuk menggunakan elemen template ini, Anda perlu membuat kueri template, mendapatkan kontennya, dan meng-clone node tersebut dengan templateContent.cloneNode
tempat argumen true
melakukan clone mendalam. Anda kemudian melakukan inisialisasi dom dengan data.
Selamat, Anda sekarang memiliki Komponen Web! Sayangnya belum ada tindakan apa pun, jadi selanjutnya, tambahkan beberapa fungsi.
6. Menambahkan Fungsi
Binding Properti
Saat ini, satu-satunya cara untuk menetapkan rating pada elemen rating adalah dengan membuat elemen, menetapkan properti rating
pada objek, lalu menempatkannya pada halaman. Sayangnya, ini bukan cara kerja elemen HTML native. Elemen HTML native cenderung diperbarui baik dengan perubahan properti maupun atribut.
Buat elemen kustom memperbarui tampilan saat properti rating
berubah dengan menambahkan baris berikut:
index.js
constructor() {
super();
this._rating = 0;
}
set rating(value) {
this._rating = value;
if (!this.shadowRoot) {
return;
}
const ratingEl = this.shadowRoot.querySelector('.rating');
if (ratingEl) {
ratingEl.innerText = this._rating;
}
}
get rating() {
return this._rating;
}
Anda menambahkan penyetel dan pengambil untuk properti rating, lalu memperbarui teks elemen rating jika tersedia. Ini berarti jika Anda menetapkan properti peringkat pada elemen, tampilan akan diperbarui; lakukan tes cepat di konsol Alat Dev Anda!
Binding Atribut
Sekarang, perbarui tampilan saat atribut berubah; ini mirip dengan input yang memperbarui tampilannya saat Anda menyetel <input value="newValue">
. Untungnya, siklus proses Komponen Web mencakup attributeChangedCallback
. Perbarui rating dengan menambahkan baris berikut:
index.js
static get observedAttributes() {
return ['rating'];
}
attributeChangedCallback(attributeName, oldValue, newValue) {
if (attributeName === 'rating') {
const newRating = Number(newValue);
this.rating = newRating;
}
}
Agar attributeChangedCallback
dapat dipicu, Anda harus menetapkan pengambil statis untuk RatingElement.observedAttributes which defines the attributes to be observed for changes
. Anda kemudian menetapkan rating secara deklaratif di DOM. Cobalah:
index.html
<rating-element rating="5"></rating-element>
Sekarang rating akan diperbarui secara deklaratif.
Fungsi Tombol
Sekarang yang tidak ada hanyalah fungsi tombol. Perilaku komponen ini harus memungkinkan pengguna untuk memberikan satu peringkat suara naik atau turun dan memberikan masukan visual kepada pengguna. Anda bisa mengimplementasikannya dengan beberapa pemroses peristiwa dan properti refleksi, tetapi pertama-tama perbarui gaya untuk memberikan masukan visual dengan menambahkan baris berikut:
index.html
<style>
...
:host([vote=up]) .thumb_up {
fill: green;
}
:host([vote=down]) .thumb_down {
fill: red;
}
</style>
Dalam Shadow DOM, pemilih :host
merujuk pada node atau elemen khusus yang dikaitkan dengan Shadow Root. Dalam hal ini, jika atribut vote
adalah "up"
, tombol suka akan berubah menjadi hijau, tetapi jika vote
adalah "down", then it will turn the thumb-down button red
. Sekarang, terapkan logika untuk hal ini dengan membuat properti / atribut yang mencerminkan untuk vote
seperti cara Anda menerapkan rating
. Mulai dengan penyetel dan pengambil properti:
index.js
constructor() {
super();
this._rating = 0;
this._vote = null;
}
set vote(newValue) {
const oldValue = this._vote;
if (newValue === oldValue) {
return;
}
if (newValue === 'up') {
if (oldValue === 'down') {
this.rating += 2;
} else {
this.rating += 1;
}
} else if (newValue === 'down') {
if (oldValue === 'up') {
this.rating -= 2;
} else {
this.rating -= 1;
}
}
this._vote = newValue;
this.setAttribute('vote', newValue);
}
get vote() {
return this._vote;
}
Anda melakukan inisialisasi properti instance _vote
dengan null
di constructor
, dan di penyetel, periksa apakah nilai barunya berbeda. Jika ya, Anda akan menyesuaikan rating dengan semestinya dan, yang penting, merefleksikan kembali atribut vote
ke host dengan this.setAttribute
.
Selanjutnya, siapkan binding atribut:
index.js
static get observedAttributes() {
return ['rating', 'vote'];
}
attributeChangedCallback(attributeName, oldValue, newValue) {
if (attributeName === 'rating') {
const newRating = Number(newValue);
this.rating = newRating;
} else if (attributeName === 'vote') {
this.vote = newValue;
}
}
Sekali lagi, ini adalah proses yang sama dengan yang Anda lakukan pada binding atribut rating
; Anda menambahkan vote
ke observedAttributes
, dan menetapkan properti vote
di attributeChangedCallback
. Dan sekarang, tambahkan beberapa pemroses peristiwa klik untuk memberikan fungsi tombol!
index.js
constructor() {
super();
this._rating = 0;
this._vote = null;
this._boundOnUpClick = this._onUpClick.bind(this);
this._boundOnDownClick = this._onDownClick.bind(this);
}
connectedCallback() {
...
this.shadowRoot.querySelector('.thumb_up')
.addEventListener('click', this._boundOnUpClick);
this.shadowRoot.querySelector('.thumb_down')
.addEventListener('click', this._boundOnDownClick);
}
disconnectedCallback() {
this.shadowRoot.querySelector('.thumb_up')
.removeEventListener('click', this._boundOnUpClick);
this.shadowRoot.querySelector('.thumb_down')
.removeEventListener('click', this._boundOnDownClick);
}
_onUpClick() {
this.vote = 'up';
}
_onDownClick() {
this.vote = 'down';
}
Di constructor
, Anda mengikat beberapa pemroses klik ke elemen dan mempertahankan referensinya. Di connectedCallback
, Anda memproses peristiwa klik pada tombol. Di disconnectedCallback
, Anda membersihkan pemroses ini, dan pada pemroses klik itu sendiri, Anda menetapkan vote
dengan benar.
Selamat, Anda sekarang memiliki Komponen Web dengan fitur lengkap; coba klik beberapa tombol! Masalahnya sekarang adalah bahwa file JS saya sekarang mencapai 96 baris, file HTML saya 43 baris, dan kodenya cukup panjang dan penting untuk komponen sederhana seperti itu. Di sinilah proyek Lit Google berperan.
7. Lit-html
Titik Pemeriksaan Kode
Mengapa lit-html
Pertama-tama, tag <template>
berguna dan berperforma tinggi, tetapi tidak dikemas dengan logika komponen sehingga sulit mendistribusikan template dengan logika lainnya. Selain itu, cara penggunaan elemen template secara inheren meminjamkan kode imperatif, yang dalam banyak kasus, menyebabkan kode yang kurang mudah dibaca dibandingkan dengan pola coding deklaratif.
Di sinilah lit-html berperan! Lit HTML adalah sistem rendering Lit yang memungkinkan Anda menulis template HTML dalam JavaScript, lalu secara efisien merender dan merender ulang template tersebut bersama dengan data untuk membuat dan memperbarui DOM. Hal ini mirip dengan library JSX dan VDOM yang populer tetapi dapat berjalan secara native di browser dan jauh lebih efisien dalam banyak kasus.
Menggunakan Lit HTML
Selanjutnya, migrasikan Komponen Web native rating-element
untuk menggunakan template Lit yang menggunakan Literal Template yang Diberi Tag yang merupakan fungsi yang menggunakan string template sebagai argumen dengan sintaksis khusus. Lit kemudian menggunakan elemen template di balik layar untuk menyediakan rendering cepat serta menyediakan beberapa fitur sanitasi untuk keamanan. Mulai dengan memigrasikan <template>
di index.html
ke template Lit dengan menambahkan metode render()
ke komponen web:
index.js
// Dont forget to import from Lit!
import {render, html} from 'lit';
class RatingElement extends HTMLElement {
...
render() {
if (!this.shadowRoot) {
return;
}
const template = html`
<style>
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
:host([vote=up]) .thumb_up {
fill: green;
}
:host([vote=down]) .thumb_down {
fill: red;
}
</style>
<button class="thumb_down">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>`;
render(template, this.shadowRoot);
}
}
Anda juga dapat menghapus template dari index.html
. Dalam metode render ini, Anda menentukan variabel yang disebut template
dan memanggil fungsi literal template yang diberi tag html
. Anda juga akan melihat bahwa Anda telah menjalankan data binding sederhana di dalam elemen span.rating
dengan menggunakan sintaksis interpolasi literal template ${...}
. Artinya, pada akhirnya Anda tidak perlu lagi mengupdate node tersebut secara imperatif. Selain itu, Anda memanggil metode render
menyala yang secara sinkron merender template ke shadow root.
Bermigrasi ke Sintaksis Deklaratif
Setelah Anda menghapus elemen <template>
, faktorkan ulang kode untuk memanggil metode render
yang baru ditetapkan. Anda dapat memulai dengan memanfaatkan binding pemroses peristiwa lit untuk membersihkan kode pemroses:
index.js
<button
class="thumb_down"
@click=${() => {this.vote = 'down'}}>
...
<button
class="thumb_up"
@click=${() => {this.vote = 'up'}}>
Template Lit dapat menambahkan pemroses peristiwa ke node dengan sintaksis binding @EVENT_NAME yang, dalam hal ini, Anda memperbarui properti vote
setiap kali tombol ini diklik.
Selanjutnya, bersihkan kode inisialisasi pemroses peristiwa di constructor
, connectedCallback
, dan disconnectedCallback
:
index.js
constructor() {
super();
this._rating = 0;
this._vote = null;
}
connectedCallback() {
this.attachShadow({mode: 'open'});
this.render();
}
// remove disonnectedCallback and _onUpClick and _onDownClick
Anda dapat menghapus logika pemroses klik dari ketiga callback dan bahkan menghapus disconnectedCallback
seluruhnya. Anda juga dapat menghapus semua kode inisialisasi DOM dari connectedCallback
sehingga terlihat jauh lebih elegan. Ini juga berarti bahwa Anda dapat menghapus metode pemroses _onUpClick
dan _onDownClick
.
Terakhir, perbarui penyetel properti untuk menggunakan metode render
sehingga dom dapat diperbarui saat properti atau atribut berubah:
index.js
set rating(value) {
this._rating = value;
this.render();
}
...
set vote(newValue) {
const oldValue = this._vote;
if (newValue === oldValue) {
return;
}
if (newValue === 'up') {
if (oldValue === 'down') {
this.rating += 2;
} else {
this.rating += 1;
}
} else if (newValue === 'down') {
if (oldValue === 'up') {
this.rating -= 2;
} else {
this.rating -= 1;
}
}
this._vote = newValue;
this.setAttribute('vote', newValue);
// add render method
this.render();
}
Di sini, Anda dapat menghapus logika update DOM dari penyetel rating
dan menambahkan panggilan ke render
dari penyetel vote
. Template ini kini jauh lebih mudah dibaca karena Anda dapat melihat di mana binding dan pemroses peristiwa diterapkan.
Muat ulang halaman, dan Anda akan memiliki tombol rating yang berfungsi seperti ini saat suara positif ditekan.
8. LitElement
Mengapa LitElement
Beberapa masalah masih ada terkait kode. Pertama, jika Anda mengubah properti atau atribut vote
, hal ini dapat mengubah properti rating
yang akan menyebabkan pemanggilan render
dua kali. Meskipun panggilan render berulang pada dasarnya tidak beroperasi dan efisien, VM JavaScript masih menghabiskan waktu untuk memanggil fungsi itu dua kali secara sinkron. Kedua, menambahkan properti dan atribut baru adalah hal yang membosankan karena memerlukan banyak kode boilerplate. Di sinilah LitElement
berperan.
LitElement
adalah class dasar Lit untuk membuat Komponen Web yang cepat dan ringan yang dapat digunakan di berbagai framework dan lingkungan. Selanjutnya, lihat apa yang dapat dilakukan LitElement
untuk kita di rating-element
dengan mengubah implementasi untuk menggunakannya.
Menggunakan LitElement
Mulai dengan mengimpor dan membuat subclass class dasar LitElement
dari paket lit
:
index.js
import {LitElement, html, css} from 'lit';
class RatingElement extends LitElement {
// remove connectedCallback()
...
Anda mengimpor LitElement
yang merupakan class dasar baru untuk rating-element
. Selanjutnya, Anda mempertahankan impor html
dan terakhir css
yang memungkinkan kita menentukan literal template yang diberi tag css untuk matematika css, pembuatan template, dan fitur lainnya.
Selanjutnya, pindahkan gaya dari metode render ke stylesheet statis Lit:
index.js
class RatingElement extends LitElement {
static get styles() {
return css`
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
:host([vote=up]) .thumb_up {
fill: green;
}
:host([vote=down]) .thumb_down {
fill: red;
}
`;
}
...
Di sinilah sebagian besar gaya berada di Lit. Lit akan menggunakan gaya ini dan menggunakan fitur browser seperti Constructable Stylesheets untuk memberikan waktu rendering yang lebih cepat serta meneruskannya melalui polyfill Komponen Web pada browser yang lebih lama jika perlu.
Lifecycle
Lit memperkenalkan serangkaian metode callback siklus proses render di atas callback Komponen Web native. Callback ini dipicu saat properti Lit yang dideklarasikan diubah.
Untuk menggunakan fitur ini, Anda harus secara statis mendeklarasikan properti mana yang akan memicu siklus proses render.
index.js
static get properties() {
return {
rating: {
type: Number,
},
vote: {
type: String,
reflect: true,
}
};
}
// remove observedAttributes() and attributeChangedCallback()
// remove set rating() get rating()
Di sini, Anda menentukan bahwa rating
dan vote
akan memicu siklus proses rendering LitElement serta menentukan jenis yang akan digunakan untuk mengonversi atribut string menjadi properti.
<user-profile .name=${this.user.name} .age=${this.user.age}>
${this.user.family.map(member => html`
<family-member
.name=${member.name}
.relation=${member.relation}>
</family-member>`)}
</user-profile>
Selain itu, tanda reflect
di properti vote
akan otomatis memperbarui atribut vote
elemen host yang Anda picu secara manual di penyetel vote
.
Setelah memiliki blok properti statis, Anda dapat menghapus semua logika pembaruan render properti dan atribut. Ini berarti Anda dapat menghapus metode berikut:
connectedCallback
observedAttributes
attributeChangedCallback
rating
(penyetel dan pengambil)vote
(penyetel dan pengambil, tetapi menyimpan logika perubahan dari penyetel)
Yang Anda pertahankan adalah constructor
serta menambahkan metode siklus proses willUpdate
baru:
index.js
constructor() {
super();
this.rating = 0;
this.vote = null;
}
willUpdate(changedProps) {
if (changedProps.has('vote')) {
const newValue = this.vote;
const oldValue = changedProps.get('vote');
if (newValue === 'up') {
if (oldValue === 'down') {
this.rating += 2;
} else {
this.rating += 1;
}
} else if (newValue === 'down') {
if (oldValue === 'up') {
this.rating -= 2;
} else {
this.rating -= 1;
}
}
}
}
// remove set vote() and get vote()
Di sini, Anda cukup melakukan inisialisasi rating
dan vote
lalu memindahkan logika penyetel vote
ke metode siklus proses willUpdate
. Metode willUpdate
dipanggil sebelum render
setiap kali properti yang diperbarui diubah, karena LitElement mengelompokkan perubahan properti dan membuat rendering asinkron. Perubahan pada properti reaktif (seperti this.rating
) di willUpdate
tidak akan memicu panggilan siklus proses render
yang tidak perlu.
Terakhir, render
adalah metode siklus proses LitElement yang mengharuskan kita menampilkan template Lit:
index.js
render() {
return html`
<button
class="thumb_down"
@click=${() => {this.vote = 'down'}}>
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button
class="thumb_up"
@click=${() => {this.vote = 'up'}}>
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>`;
}
Anda tidak perlu lagi memeriksa root bayangan, dan tidak perlu lagi memanggil fungsi render
yang sebelumnya diimpor dari paket 'lit'
.
Elemen Anda akan dirender dalam pratinjau sekarang; klik!
9. Selamat
Selamat, Anda telah berhasil membuat Komponen Web dari awal dan mengembangkannya menjadi LitElement.
Lit sangat kecil (< 5 kb diminifikasi + gzip), sangat cepat, dan sangat menyenangkan untuk membuat kode. Anda dapat membuat komponen untuk digunakan oleh framework lain, atau Anda dapat membangun aplikasi yang lengkap dengannya.
Sekarang Anda telah mengetahui apa yang dimaksud dengan Komponen Web, cara membuatnya, dan bagaimana Lit memudahkan pembuatannya.
Titik Pemeriksaan Kode
Ingin membandingkan kode final Anda dengan kode kami? Bandingkan di sini.
Apa selanjutnya?
Lihat beberapa codelab lainnya.
- Lit for React Developers
- Membuat Penampil Bata dengan elemen lit-element
- Membuat Komponen Stories dengan lit-element
Bacaan lebih lanjut
- Tutorial interaktif singkat
- Dokumen Lit
- Komponen Web Terbuka - Komunitas panduan dan alat yang dijalankan oleh komunitas
- WebComponents.dev - Membuat Komponen Web di semua framework yang dikenal
Komunitas
- Lit and Friends Slack - Komunitas Komponen Web terbesar
- @buildWithLit di Twitter - Akun Twitter tim yang membuat Lit
- SF Komponen Web - Pertemuan Komponen Web untuk San Francisco