Pengertian Lit
Lit merupakan kumpulan library open source dari Google yang dapat membantu developer membuat komponen yang cepat dan ringan serta berfungsi di semua framework. Dengan bantuan Lit, Anda dapat membuat komponen yang bisa dibagikan, membuat aplikasi, sistem desain, dan masih banyak lagi.
Yang akan Anda pelajari
Cara menerjemahkan beberapa konsep React ke Lit:
- JSX & Pembuatan Template
- Komponen & Properti
- Status & Siklus Proses
- Hook
- Turunan
- Referensi
- Status Mediasi
Yang akan Anda buat
Di akhir codelab ini, Anda dapat mengonversi konsep komponen React ke analog Lit.
Yang dibutuhkan
- Chrome, Safari, Firefox, atau Edge versi terbaru.
- Pengetahuan tentang HTML, CSS, JavaScript, dan Chrome DevTools.
- Pengetahuan tentang React
- (Lanjutan) Jika Anda menginginkan pengalaman pengembangan terbaik, download VS Code. Anda juga akan memerlukan lit-plugin untuk VS Code dan NPM.
Konsep dan kemampuan inti Lit hampir serupa dengan React dalam berbagai hal. Namun, Lit juga memiliki beberapa perbedaan dan pembeda utama:
Kecil
Ukuran Lit sangat kecil yaitu sekitar 5 kb yang diperkecil dan di-gzip dibandingkan dengan ukuran 40+ kb dari React + ReactDOM.
Cepat
Pada tolok ukur publik yang membandingkan sistem pembuatan template Lit, lit-html, hingga VDOM React, lit-html muncul 8-10% lebih cepat daripada React dalam kasus terburuk dan 50%+ lebih cepat dalam kasus penggunaan yang lebih umum.
LitElement (class dasar komponen Lit) menambah overhead minimal untuk lit-html, tetapi mengalahkan performa React sebesar 16-30% saat membandingkan fitur komponen seperti penggunaan memori, interaksi, serta waktu startup.
Tidak memerlukan build
Dengan fitur browser baru seperti modul ES dan literal template yang diberi tag, Lit tidak memerlukan kompilasi untuk berjalan. Hal ini berarti lingkungan developer dapat disiapkan dengan tag skrip + browser + server, dan Anda siap untuk memulai.
Dengan modul ES dan CDN modern seperti Skypack atau UNPKG, Anda mungkin tidak memerlukan NPM untuk memulai.
Namun, jika mau, Anda tetap dapat membuat dan mengoptimalkan kode Lit. Konsolidasi developer terbaru seputar modul ES asli telah sesuai untuk Lit – Lit hanyalah JavaScript biasa dan tidak perlu untuk CLI khusus framework atau penanganan build.
Agnostik framework
Komponen Lit dibuat dari serangkaian standar web yang disebut Komponen Web. Hal ini berarti bahwa membuat komponen di Lit akan berfungsi di framework saat ini ataupun nanti. Jika komponen ini mendukung elemen HTML, komponen ini juga akan mendukung komponen Web.
Satu-satunya masalah interop framework adalah saat framework memiliki dukungan terbatas untuk DOM. React adalah salah satu dari framework ini, tetapi memungkinkan jalan keluar melalui Ref, dan Ref di React bukanlah pengalaman developer yang baik.
Tim Lit telah mengerjakan project eksperimental bernama @lit-labs/react
yang akan mengurai komponen Lit Anda secara otomatis dan membuat wrapper React agar Anda tidak perlu menggunakan ref.
Sebagai tambahan, Custom Elements Everywhere akan menunjukkan framework dan library yang berfungsi dengan baik dengan elemen kustom.
Dukungan TypeScript kelas satu
Meskipun mungkin untuk menulis semua kode Lit di JavaScript, Lit akan ditulis dalam TypeScript dan tim Lit menyarankan agar developer juga menggunakan TypeScript.
Tim Lit telah bekerja sama dengan komunitas Lit untuk membantu mengelola project yang menghadirkan pemeriksaan jenis TypeScript dan intellisense ke template Lit pada saat pengembangan dan waktu build dengan lit-analyzer
dan lit-plugin
.
Fitur pengembangan tertanam dalam browser
Komponen lit hanyalah elemen HTML di DOM. Hal ini berarti untuk memeriksakan komponen, Anda tidak perlu menginstal alat atau ekstensi apa pun untuk browser.
Anda dapat membuka alat developer, memilih elemen, dan menjelajahi properti ataupun statusnya.
, $0.value menampilkan halo dunia, $0.outlined menampilkan nilai syarat benar, dan {$0} menunjukkan perluasan properti" class="l10n-relative-url-src" l10n-attrs-original-order="alt,src,class" src="https://codelabs.developers.google.com/codelabs/lit-2-for-react-devs/./img/browser-tools.png" />
Layanan ini dibuat dengan mempertimbangkan rendering sisi server (SSR)
Lit 2 dibuat dengan mempertimbangkan dukungan SSR. Pada saat menulis codelab ini, tim Lit belum merilis alat SSR dalam bentuk yang stabil, tetapi tim Lit telah men-deploy komponen yang dirender sisi server di seluruh produk Google. Tim Lit berharap dapat merilis fitur ini secara eksternal di GitHub dengan segera.
Sementara itu, Anda dapat mengikuti kemajuan tim Lit di sini.
Persetujuan rendah
Lit tidak memerlukan komitmen yang signifikan untuk digunakan. Anda dapat membuat komponen di Lit dan menambahkannya ke project yang ada. Jika tidak menyukainya, Anda tidak perlu mengonversi seluruh aplikasi sekaligus karena komponen web dapat berfungsi dalam framework lain.
Sudahkah Anda membuat seluruh aplikasi di Lit dan apakah juga ingin mengubah ke bentuk lain? Selanjutnya Anda dapat menempatkan aplikasi Lit saat ini di dalam framework baru dan memigrasikan apa pun yang Anda inginkan ke komponen framework baru.
Sebagai tambahan, banyak framework modern mendukung output di komponen web, sehingga kerangka tersebut biasanya dapat masuk ke dalam elemen Lit itu sendiri.
Ada dua cara untuk melakukan codelab ini:
- Anda dapat melakukannya sepenuhnya secara online di browser
- (Lanjutan) Anda dapat melakukannya di komputer lokal menggunakan VS Code.
Mengakses kode
Pada seluruh codelab, terdapat link ke playground Lit seperti ini:
Playground adalah sandbox kode yang berjalan sepenuhnya di browser Anda. Sandbox kode ini dapat mengompilasi dan menjalankan file TypeScript dan JavaScript, dan juga dapat menyelesaikan impor ke modul node secara otomatis, misalnya:
// before
import './my-file.js';
import 'lit';
// after
import './my-file.js';
import 'https://cdn.skypack.dev/lit';
Anda dapat melakukan seluruh tutorial di playground Lit dengan menggunakan checkpoint ini sebagai titik awal. Jika menggunakan VS Code, Anda dapat menggunakan checkpoint ini untuk mendownload kode awal pada langkah apa pun, serta menggunakannya untuk memeriksa pekerjaan Anda.
Menjelajahi UI playground lit
Screenshot UI playground menyorot 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
- Intellisense template & pelengkapan otomatis
Jika Anda memiliki NPM, VS Code (dengan plugin lit-plugin) yang sudah terinstal dan mengetahui cara menggunakannya, Anda dapat mendownload dan memulai project ini dengan melakukan hal berikut:
- Tekan tombol download
- Ekstrak konten file tar ke dalam direktori
- (Jika TS) menyiapkan tsconfig cepat yang menghasilkan modul es dan es2015+
- Instal server dev yang dapat menyelesaikan penentu modul kosong (tim Lit menyarankan @web/dev-server)
- Jalankan server dev dan buka browser (jika menggunakan @web/dev-server, Anda dapat menggunakan
web-dev-server --node-resolve --watch --open
)
Di bagian ini, Anda akan mempelajari dasar-dasar template di Lit.
JSX & Template Lit
JSX adalah ekstensi sintaksis untuk JavaScript yang memungkinkan pengguna React menulis templatenya dengan mudah di kode JavaScript. Template Lit memiliki tujuan yang serupa: menunjukkan UI komponen sebagai fungsi dari statusnya.
Sintaksis Dasar
Di React, Anda akan merender JSX halo dunia seperti ini:
import 'react';
import ReactDOM from 'react-dom';
const name = 'Josh Perez';
const element = (
<>
<h1>Hello, {name}</h1>
<div>How are you?</div>
</>
);
ReactDOM.render(
element,
mountNode
);
Pada contoh di atas, ada dua elemen dan variabel "name" yang disertakan. Di Lit, Anda akan melakukan hal berikut:
import {html, render} from 'lit';
const name = 'Josh Perez';
const element = html`
<h1>Hello, ${name}</h1>
<div>How are you?</div>`;
render(
element,
mountNode
);
Perlu diperhatikan bahwa template Lit tidak memerlukan React Fragment untuk mengelompokkan beberapa elemen dalam template.
Di Lit, template digabungkan dengan html
template yang diberi tag LIT eral yang merupakan tempat Lit mendapatkan namanya.
Nilai Template
Template Lit dapat menerima template Lit lainnya, yang disebut TemplateResult
. Misalnya, gabungkan name
dalam tag miring (<i>
) lalu gabungkan kode tersebut dengan literal template yang diberi tag NB. Pastikan untuk menggunakan karakter backtick (`
) bukan karakter kutipan tunggal ('
).
import {html, render} from 'lit';
const name = html`<i>Josh Perez</i>`;
const element = html`
<h1>Hello, ${name}</h1>
<div>How are you?</div>`;
render(
element,
mountNode
);
Lit TemplateResult
dapat menerima array, string, TemplateResult
lainnya, serta perintah.
Untuk latihan, coba konversi kode React berikut menjadi Lit:
const itemsToBuy = [
<li>Bananas</li>,
<li>oranges</li>,
<li>apples</li>,
<li>grapes</li>
];
const element = (
<>
<h1>Things to buy:</h1>
<ol>
{itemsToBuy}
</ol>
</>);
ReactDOM.render(
element,
mountNode
);
Jawaban:
import {html, render} from 'lit';
const itemsToBuy = [
html`<li>Bananas</li>`,
html`<li>oranges</li>`,
html`<li>apples</li>`,
html`<li>grapes</li>`
];
const element = html`
<h1>Things to buy:</h1>
<ol>
${itemsToBuy}
</ol>`;
render(
element,
mountNode
);
Meneruskan dan menyetel properti
Salah satu perbedaan terbesar antara sintaksis JSX dan Lit adalah sintaksis data binding. Misalnya, ambil input React ini dengan binding:
const disabled = false;
const label = 'my label';
const myClass = 'my-class';
const value = 'my value';
const element =
<input
disabled={disabled}
className={`static-class ${myClass}`}
defaultValue={value}/>;
ReactDOM.render(
element,
mountNode
);
Pada contoh di atas, input ditentukan dengan melakukan hal berikut:
- Menetapkan penonaktifan untuk variabel yang ditentukan (salah dalam kasus ini)
- Menetapkan class ke
static-class
plus variabel (dalam kasus ini"static-class my-class"
) - Menetapkan nilai default
Di Lit, Anda akan melakukan hal berikut:
import {html, render} from 'lit';
const disabled = false;
const label = 'my label';
const myClass = 'my-class';
const value = 'my value';
const element = html`
<input
?disabled=${disabled}
class="static-class ${myClass}"
.value=${value}>`;
render(
element,
mountNode
);
Pada contoh Lit, binding boolean ditambahkan untuk mengalihkan atribut disabled
.
Berikutnya, terdapat binding langsung ke atribut class
daripada className
. Beberapa binding dapat ditambahkan ke atribut class
, kecuali Anda menggunakan perintah classMap
yang merupakan bantuan deklaratif untuk mengalihkan class.
Terakhir, properti value
disetel di input. Tidak seperti React, Lit tidak akan menyetel elemen input menjadi hanya-baca karena mengikuti implementasi asli dan perilaku input.
Sintaksis binding properti Lit
html`<my-element ?attribute-name=${booleanVar}>`;
- Awalan
?
adalah sintaksis binding untuk mengalihkan atribut pada elemen - Setara dengan
inputRef.toggleAttribute('attribute-name', booleanVar)
- Berguna untuk elemen yang menggunakan
disabled
sebagaidisabled="false"
masih dibaca sebagai true oleh DOM karenainputElement.hasAttribute('disabled') === true
html`<my-element .property-name=${anyVar}>`;
- Awalan
.
adalah sintaksis binding untuk menyetel properti elemen - Setara dengan
inputRef.propertyName = anyVar
- Cocok untuk meneruskan data kompleks seperti objek, array, atau class
html`<my-element attribute-name=${stringVar}>`;
- Menggabungkan ke atribut elemen
- Setara dengan
inputRef.setAttribute('attribute-name', stringVar)
- Cocok untuk nilai dasar, pemilih aturan gaya, dan querySelectors
Meneruskan pengendali
const disabled = false;
const label = 'my label';
const myClass = 'my-class';
const value = 'my value';
const element =
<input
onClick={() => console.log('click')}
onChange={e => console.log(e.target.value)} />;
ReactDOM.render(
element,
mountNode
);
Pada contoh di atas, input ditentukan dengan melakukan hal berikut:
- Log kata "klik" saat input diklik
- Log nilai input saat pengguna mengetik karakter
Di Lit, Anda akan melakukan hal berikut:
import {html, render} from 'lit';
const disabled = false;
const label = 'my label';
const myClass = 'my-class';
const value = 'my value';
const element = html`
<input
@click=${() => console.log('click')}
@input=${e => console.log(e.target.value)}>`;
render(
element,
mountNode
);
Pada contoh Lit, terdapat pemroses yang ditambahkan ke peristiwa click
dengan @click
.
Berikutnya, daripada menggunakan onChange
, binding ke peristiwa input
native dari <input>
tersedia karena peristiwa change
asli hanya dilepaskan di blur
(abstrak React pada peristiwa ini),
Sintaksis pengendali peristiwa Lit
html`<my-element @event-name=${() => {...}}></my-element>`;
- Awalan
@
adalah sintaksis binding untuk pemrosesan peristiwa - Setara dengan
inputRef.addEventListener('event-name', ...)
- Menggunakan nama peristiwa DOM asli
Di bagian ini, Anda akan mempelajari tentang komponen dan fungsi class Lit. Status dan Hook dibahas secara lebih mendetail di bagian selanjutnya.
Komponen Class & LitElement
Lit yang setara dengan komponen class React adalah LitElement, dan konsep Lit tentang "properti reaktif" adalah kombinasi dari properti dan status React. Contoh:
import React from 'react';
import ReactDOM from 'react-dom';
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {name: ''};
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
const element = <Welcome name="Elliott"/>
ReactDOM.render(
element,
mountNode
);
Pada contoh di atas, terdapat komponen React yang:
- Merender
name
- Menetapkan nilai default
name
ke string kosong (""
) - Menetapkan ulang
name
menjadi"Elliott"
Ini merupakan cara yang akan Anda lakukan di LitEement
Di TypeScript:
import {LitElement, html} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('welcome-banner')
class WelcomeBanner extends LitElement {
@property({type: String})
name = '';
render() {
return html`<h1>Hello, ${this.name}</h1>`
}
}
Di JavaScript:
import {LitElement, html} from 'lit';
class WelcomeBanner extends LitElement {
static get properties() {
return {
name: {type: String}
}
}
constructor() {
super();
this.name = '';
}
render() {
return html`<h1>Hello, ${this.name}</h1>`
}
}
customElements.define('welcome-banner', WelcomeBanner);
Dan di file HTML:
<!-- index.html -->
<head>
<script type="module" src="./index.js"></script>
</head>
<body>
<welcome-banner name="Elliott"></welcome-banner>
</body>
Tinjauan tentang apa yang terjadi dalam contoh di atas:
@property({type: String})
name = '';
- Menentukan properti reaktif publik – bagian dari API publik komponen
- Mengekspos atribut (secara default) serta properti pada komponen
- Menentukan cara menerjemahkan atribut komponen (yang berupa string) ke dalam nilai
static get properties() {
return {
name: {type: String}
}
}
- Fungsi di bawah ini sama seperti dekorator TS
@property
, tetapi berjalan secara asli di JavaScript
render() {
return html`<h1>Hello, ${this.name}</h1>`
}
- Fungsi ini disebut setiap kali properti reaktif diubah
@customElement('welcome-banner')
class WelcomeBanner extends LitElement {
...
}
- Fungsi ini akan mengaitkan nama tag Elemen HTML dengan definisi class
- Karena standar Elemen Kustom, nama tag harus menyertakan tanda hubung (-)
this
dalam LitElement merujuk pada instance elemen kustom (dalam kasus ini<welcome-banner>
)
customElements.define('welcome-banner', WelcomeBanner);
- Fungsi di bawah ini merupakan JavaScript yang setara dengan dekorasi TS
@customElement
<head>
<script type="module" src="./index.js"></script>
</head>
- Mengimpor definisi elemen kustom
<body>
<welcome-banner name="Elliott"></welcome-banner>
</body>
- Menambahkan elemen kustom ke halaman
- Menetapkan properti
name
ke'Elliott'
Komponen Fungsi
Lit tidak memiliki penafsiran 1:1 komponen fungsi karena tidak menggunakan JSX atau praprosesor. Meskipun begitu, cukup mudah untuk menuliskan fungsi yang membutuhkan properti dan merender DOM berdasarkan properti tersebut. Contoh:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Elliott"/>
ReactDOM.render(
element,
mountNode
);
Dalam Lit, fungsi ini akan menjadi:
import {html, render} from 'lit';
function Welcome(props) {
return html`<h1>Hello, ${props.name}</h1>`;
}
render(
Welcome({name: 'Elliott'}),
document.body.querySelector('#root')
);
Di bagian ini, Anda akan mempelajari status dan siklus proses Lit.
Status
Konsep Lit tentang "Properti Reaktif" adalah campuran status dan properti React. Jika Properti Reaktif diubah dapat memicu siklus proses komponen. Properti reaktif tersedia dalam dua varian:
Properti reaktif publik
// React
import React from 'react';
class MyEl extends React.Component {
constructor(props) {
super(props)
this.state = {name: 'there'}
}
componentWillReceiveProps(nextProps) {
if (this.props.name !== nextProps.name) {
this.setState({name: nextProps.name})
}
}
}
// Lit (TS)
import {LitElement} from 'lit';
import {property} from 'lit/decorators.js';
class MyEl extends LitElement {
@property() name = 'there';
}
- Ditentukan oleh
@property
- Mirip dengan properti dan status React, tetapi dapat diubah
- API Publik yang diakses dan ditetapkan oleh konsumen komponen
Status reaktif internal
// React
import React from 'react';
class MyEl extends React.Component {
constructor(props) {
super(props)
this.state = {name: 'there'}
}
}
// Lit (TS)
import {LitElement} from 'lit';
import {state} from 'lit/decorators.js';
class MyEl extends LitElement {
@state() name = 'there';
}
- Ditentukan oleh
@state
- Serupa dengan status React, tetapi dapat diubah
- Status internal pribadi yang biasanya diakses dari dalam komponen atau subclass
Siklus Proses
Siklus proses Lit cukup mirip dengan React, tetapi ada beberapa perbedaan yang signifikan.
constructor
// React (js)
import React from 'react';
class MyEl extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this._privateProp = 'private';
}
}
// Lit (ts)
class MyEl extends LitElement {
@property({type: Number}) counter = 0;
private _privateProp = 'private';
}
// Lit (js)
class MyEl extends LitElement {
static get properties() {
return { counter: {type: Number} }
}
constructor() {
this.counter = 0;
this._privateProp = 'private';
}
}
- Lit setara juga dengan
constructor
- Tidak perlu meneruskan apa pun ke panggilan super
- Dijalankan oleh (tidak sepenuhnya inklusif):
document.createElement
document.innerHTML
new ComponentClass()
- Jika nama tag yang belum diupgrade berada di halaman, definisi dimuat dan didaftarkan dengan
@customElement
ataucustomElements.define
- Serupa dengan fungsi untuk
constructor
React
render
// React
render() {
return <div>Hello World</div>
}
// Lit
render() {
return html`<div>Hello World</div>`;
}
- Lit setara juga dengan
render
- Dapat menampilkan hasil yang dapat dirender, misalnya
TemplateResult
ataustring
dll. - Agar serupa dengan React,
render()
harus berupa fungsi murni - Akan merender ke node mana pun yang ditampilkan
createRenderRoot()
(ShadowRoot
secara default)
componentDidMount
componentDidMount
mirip dengan kombinasi dari callback siklus proses firstUpdated
dan connectedCallback
Lit.
firstUpdated
import Chart from 'chart.js';
// React
componentDidMount() {
this._chart = new Chart(this.chartElRef.current, {...});
}
// Lit
firstUpdated() {
this._chart = new Chart(this.chartEl, {...});
}
- Dipanggil saat template komponen pertama kali dirender ke root komponen
- Hanya akan dipanggil jika elemen terhubung misalnya tidak dipanggil melalui
document.createElement('my-component')
sampai node tersebut ditambahkan ke hierarki DOM - Fungsi ini merupakan tempat yang baik untuk melakukan penyiapan komponen yang memerlukan DOM yang dirender oleh komponen
- Tidak seperti React,
componentDidMount
yang berubah ke properti reaktif difirstUpdated
akan menyebabkan perenderan ulang, meskipun browser biasanya akan mengelompokkan perubahan ke dalam frame yang sama. Jika perubahan tersebut tidak memerlukan akses ke DOM root, biasanya perubahan itu akan berada diwillUpdate
connectedCallback
// React
componentDidMount() {
this.window.addEventListener('resize', this.boundOnResize);
}
// Lit
connectedCallback() {
super.connectedCallback();
this.window.addEventListener('resize', this.boundOnResize);
}
- Dipanggil setiap kali elemen kustom disisipkan ke dalam hierarki DOM
- Tidak seperti komponen React, saat elemen kustom dilepaskan dari DOM, elemen tersebut tidak akan dihancurkan sehingga dapat "dihubungkan" beberapa kali
- Berguna untuk menginisialisasi ulang DOM atau melampirkan ulang pemroses peristiwa yang telah dihapus saat diputuskan
- Catatan:
connectedCallback
mungkin dipanggil sebelumfirstUpdated
sehingga pada panggilan pertama, DOM mungkin tidak akan tersedia
componentDidUpdate
// React
componentDidUpdate(prevProps) {
if (this.props.title !== prevProps.title) {
this._chart.setTitle(this.props.title);
}
}
// Lit (ts)
updated(prevProps: PropertyValues<this>) {
if (prevProps.has('title')) {
this._chart.setTitle(this.title);
}
}
- Persamaan Lit adalah
updated
(menggunakan bentuk waktu lampau bahasa Inggris dari "update") - Tidak seperti React,
updated
juga dipanggil pada render awal - Serupa dengan fungsi untuk
componentDidUpdate
React
componentWillUnmount
// React
componentWillUnmount() {
this.window.removeEventListener('resize', this.boundOnResize);
}
// Lit
disconnectedCallback() {
super.disconnectedCallback();
this.window.removeEventListener('resize', this.boundOnResize);
}
- Persamaan Lit setara dengan
disconnectedCallback
- Tidak seperti komponen React, saat elemen kustom dilepaskan dari DOM, komponen tidak akan dimusnahkan
- Tidak seperti
componentWillUnmount
,disconnectedCallback
dipanggil setelah elemen dihapus dari hierarki - DOM di dalam root masih terpasang ke subhierarki root
- Berguna untuk membersihkan pemroses peristiwa dan referensi yang bocor sehingga browser dapat membersihkan sampah memori komponen
Latihan
import React from 'react';
import ReactDOM from 'react-dom';
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Pada contoh di atas, terdapat jam sederhana yang melakukan hal berikut:
- Jam tersebut merender "Halo Dunia! Waktunya" lalu menampilkan waktu
- Setiap detik fungsi tersebut akan memperbarui jam
- Ketika dilepaskan, tindakan tersebut akan menghapus interval yang memanggil tick
Pertama-tama, mulai dengan deklarasi class komponen:
// Lit (TS)
// some imports here are imported in advance
import {LitElement, html} from 'lit';
import {customElement, state} from 'lit/decorators.js';
@customElement('lit-clock')
class LitClock extends LitElement {
}
// Lit (JS)
// `html` is imported in advance
import {LitElement, html} from 'lit';
class LitClock extends LitElement {
}
customElements.define('lit-clock', LitClock);
Berikutnya, lakukan inisialisasi date
dan deklarasikan properti reaktif internal dengan @state
karena pengguna komponen tidak akan menetapkan date
secara langsung.
// Lit (TS)
import {LitElement, html} from 'lit';
import {customElement, state} from 'lit/decorators.js';
@customElement('lit-clock')
class LitClock extends LitElement {
@state() // declares internal reactive prop
private date = new Date(); // initialization
}
// Lit (JS)
import {LitElement, html} from 'lit';
class LitClock extends LitElement {
static get properties() {
return {
// declares internal reactive prop
date: {state: true}
}
}
constructor() {
super();
// initialization
this.date = new Date();
}
}
customElements.define('lit-clock', LitClock);
Berikutnya, merender template.
// Lit (JS & TS)
render() {
return html`
<div>
<h1>Hello, World!</h1>
<h2>It is ${this.date.toLocaleTimeString()}.</h2>
</div>
`;
}
Sekarang, terapkan metode tick.
tick() {
this.date = new Date();
}
Selanjutnya adalah penerapan componentDidMount
. Sekali lagi, analog Lit adalah campuran dari firstUpdated
dan connectedCallback
. Dalam kasus komponen ini, memanggil tick
dengan setInterval
tidak memerlukan akses ke DOM di dalam root. Selain itu, interval akan dikosongkan ketika elemen dihapus dari hierarki dokumen sehingga jika dipasang kembali, interval harus memulai lagi. Jadi, connectedCallback
adalah pilihan yang lebih baik di sini.
// Lit (TS)
@customElement('lit-clock')
class LitClock extends LitElement {
@state()
private date = new Date();
private timerId = -1; // initialize timerId for TS
connectedCallback() {
super.connectedCallback();
this.timerId = setInterval(
() => this.tick(),
1000
);
}
...
}
// Lit (JS)
constructor() {
super();
// initialization
this.date = new Date();
this.timerId = -1; // initialize timerId for JS
}
connectedCallback() {
super.connectedCallback();
this.timerId = setInterval(
() => this.tick(),
1000
);
}
Terakhir, bersihkan interval agar tidak menjalankan tick setelah elemen terputus dari hierarki dokumen.
// Lit (TS & JS)
disconnectedCallback() {
super.disconnectedCallback();
clearInterval(this.timerId);
}
Jika menggabungkan semuanya, elemen tersebut akan terlihat seperti ini:
// Lit (TS)
import {LitElement, html} from 'lit';
import {customElement, state} from 'lit/decorators.js';
@customElement('lit-clock')
class LitClock extends LitElement {
@state()
private date = new Date();
private timerId = -1;
connectedCallback() {
super.connectedCallback();
this.timerId = setInterval(
() => this.tick(),
1000
);
}
tick() {
this.date = new Date();
}
render() {
return html`
<div>
<h1>Hello, World!</h1>
<h2>It is ${this.date.toLocaleTimeString()}.</h2>
</div>
`;
}
disconnectedCallback() {
super.disconnectedCallback();
clearInterval(this.timerId);
}
}
// Lit (JS)
import {LitElement, html} from 'lit';
class LitClock extends LitElement {
static get properties() {
return {
date: {state: true}
}
}
constructor() {
super();
this.date = new Date();
}
connectedCallback() {
super.connectedCallback();
this.timerId = setInterval(
() => this.tick(),
1000
);
}
tick() {
this.date = new Date();
}
render() {
return html`
<div>
<h1>Hello, World!</h1>
<h2>It is ${this.date.toLocaleTimeString()}.</h2>
</div>
`;
}
disconnectedCallback() {
super.disconnectedCallback();
clearInterval(this.timerId);
}
}
customElements.define('lit-clock', LitClock);
Di bagian ini, Anda akan mempelajari cara menerjemahkan konsep React Hook ke Lit.
Konsep hook React
Hook React memberikan cara untuk membuat komponen fungsi "hook" ke dalam status. Ada beberapa manfaat dari hook ini.
- Hook menyederhanakan penggunaan kembali dari logika stateful
- Membantu membagi komponen ke dalam beberapa fungsi yang lebih kecil
Selain itu, fokus pada komponen berbasis fungsi mengatasi masalah tertentu terkait sintaksis berbasis class React seperti:
- Harus meneruskan
props
dariconstructor
kesuper
- Melakukan inisialisasi properti yang tidak rapi di
constructor
- Hook ini merupakan alasan yang dinyatakan oleh tim React pada saat itu tetapi diselesaikan oleh ES2019
- Masalah yang disebabkan oleh
this
tidak lagi mengacu pada komponen
Konsep hook React di Lit
Seperti yang disebutkan di bagian Komponen & Properti, Lit tidak menawarkan cara untuk membuat elemen kustom dari fungsi, tetapi LitElement dapat menangani sebagian besar masalah utama pada komponen class React. Contoh:
// React (at the time of making hooks)
import React from 'react';
import ReactDOM from 'react-dom';
class MyEl extends React.Component {
constructor(props) {
super(props); // Leaky implementation
this.state = {count: 0};
this._chart = null; // Deemed messy
}
render() {
return (
<>
<div>Num times clicked {count}</div>
<button onClick={this.clickCallback}>click me</button>
</>
);
}
clickCallback() {
// Errors because `this` no longer refers to the component
this.setState({count: this.count + 1});
}
}
// Lit (ts)
class MyEl extends LitElement {
@property({type: Number}) count = 0; // No need for constructor to set state
private _chart = null; // Public class fields introduced to JS in 2019
render() {
return html`
<div>Num times clicked ${count}</div>
<button @click=${this.clickCallback}>click me</button>`;
}
private clickCallback() {
// No error because `this` refers to component
this.count++;
}
}
Bagaimana cara Lit mengatasi masalah ini?
constructor
tidak memerlukan argumen- Semua binding
@event
otomatis dikelompokkan kethis
this
dalam kebanyakan kasus mengacu pada referensi elemen kustom- Properti class sekarang dapat dibuat instance sebagai anggota class. Tindakan ini akan menghapus penerapan berbasis konstruktor
Pengontrol Reaktif
Konsep utama di balik Hook yang ada di Lit sebagai pengontrol reaktif. Pola pengontrol reaktif memungkinkan untuk berbagi logika stateful, membagi komponen menjadi lebih kecil, lebih banyak bit modular, serta menghubungkan ke siklus proses update dari suatu elemen.
Pengontrol reaktif adalah antarmuka objek yang dapat terhubung ke siklus proses update dari host pengontrol seperti LitElement.
Siklus proses dari ReactiveController
dan reactiveControllerHost
adalah:
interface ReactiveController {
hostConnected(): void;
hostUpdate(): void;
hostUpdated(): void;
hostDisconnected(): void;
}
interface ReactiveControllerHost {
addController(controller: ReactiveController): void;
removeController(controller: ReactiveController): void;
requestUpdate(): void;
readonly updateComplete: Promise<boolean>;
}
Dengan membuat pengontrol reaktif dan melampirkannya ke host dengan addController
, siklus proses pengontrol akan dipanggil bersama host tersebut. Misalnya, memanggil kembali contoh jam dari bagian Status & Siklus Proses:
import React from 'react';
import ReactDOM from 'react-dom';
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Pada contoh di atas, terdapat jam sederhana yang melakukan hal berikut:
- Jam tersebut merender "Halo Dunia! Waktunya" lalu menampilkan waktu
- Setiap detik fungsi tersebut akan memperbarui jam
- Ketika dilepaskan, tindakan tersebut akan menghapus interval yang memanggil tick
Membuat perancah komponen
Mulailah dengan deklarasi class komponen dan tambahkan fungsi render
.
// Lit (TS) - index.ts
import {LitElement, html} from 'lit';
import {customElement} from 'lit/decorators.js';
@customElement('my-element')
class MyElement extends LitElement {
render() {
return html`
<div>
<h1>Hello, world!</h1>
<h2>It is ${'time to get Lit'}.</h2>
</div>
`;
}
}
// Lit (JS) - index.js
import {LitElement, html} from 'lit';
class MyElement extends LitElement {
render() {
return html`
<div>
<h1>Hello, world!</h1>
<h2>It is ${'time to get Lit'}.</h2>
</div>
`;
}
}
customElements.define('my-element', MyElement);
Membuat pengontrol
Sekarang beralih ke clock.ts
dan buat class untuk ClockController
lalu siapkan constructor
:
// Lit (TS) - clock.ts
import {ReactiveController, ReactiveControllerHost} from 'lit';
export class ClockController implements ReactiveController {
private readonly host: ReactiveControllerHost;
constructor(host: ReactiveControllerHost) {
this.host = host;
host.addController(this);
}
hostConnected() {
}
private tick() {
}
hostDisconnected() {
}
// Will not be used but needed for TS compilation
hostUpdate() {};
hostUpdated() {};
}
// Lit (JS) - clock.js
export class ClockController {
constructor(host) {
this.host = host;
host.addController(this);
}
hostConnected() {
}
tick() {
}
hostDisconnected() {
}
}
Pengontrol reaktif dapat dibuat dengan cara apa pun asalkan dapat membagikan antarmuka ReactiveController
. Namun, menggunakan class dengan constructor
yang dapat menggunakan antarmuka ReactiveControllerHost
serta properti lain yang diperlukan untuk menginisialisasi pengontrol adalah pola yang lebih dipilih oleh tim Lit untuk digunakan di sebagian besar kasus dasar.
Sekarang Anda perlu menerjemahkan callback siklus proses React menjadi callback pengontrol. Singkatnya:
componentDidMount
- Ke
connectedCallback
LitElement - Ke
hostConnected
pengontrol
- Ke
ComponentWillUnmount
- Ke
disconnectedCallback
LitElement - Ke
hostDisconnected
pengontrol
- Ke
Untuk mengetahui informasi selengkapnya tentang cara menerjemahkan siklus proses React ke siklus proses Lit, lihat bagian Status & Siklus Proses.
Berikutnya, terapkan metode callback hostConnected
dan tick
, serta bersihkan interval di hostDisconnected
seperti yang dilakukan pada contoh di bagian Status & Siklus Proses.
// Lit (TS) - clock.ts
export class ClockController implements ReactiveController {
private readonly host: ReactiveControllerHost;
private interval = 0;
date = new Date();
constructor(host: ReactiveControllerHost) {
this.host = host;
host.addController(this);
}
hostConnected() {
this.interval = setInterval(() => this.tick(), 1000);
}
private tick() {
this.date = new Date();
}
hostDisconnected() {
clearInterval(this.interval);
}
hostUpdate() {};
hostUpdated() {};
}
// Lit (JS) - clock.js
export class ClockController {
interval = 0;
host;
date = new Date();
constructor(host) {
this.host = host;
host.addController(this);
}
hostConnected() {
this.interval = setInterval(() => this.tick(), 1000);
}
tick() {
this.date = new Date();
}
hostDisconnected() {
clearInterval(this.interval);
}
}
Menggunakan pengontrol
Untuk menggunakan pengontrol jam, impor pengontrol, lalu perbarui komponen di index.ts
atau index.js
.
// Lit (TS) - index.ts
import {LitElement, html, ReactiveController, ReactiveControllerHost} from 'lit';
import {customElement} from 'lit/decorators.js';
import {ClockController} from './clock.js';
@customElement('my-element')
class MyElement extends LitElement {
private readonly clock = new ClockController(this); // Instantiate
render() {
// Use controller
return html`
<div>
<h1>Hello, world!</h1>
<h2>It is ${this.clock.date.toLocaleTimeString()}.</h2>
</div>
`;
}
}
// Lit (JS) - index.js
import {LitElement, html} from 'lit';
import {ClockController} from './clock.js';
class MyElement extends LitElement {
clock = new ClockController(this); // Instantiate
render() {
// Use controller
return html`
<div>
<h1>Hello, world!</h1>
<h2>It is ${this.clock.date.toLocaleTimeString()}.</h2>
</div>
`;
}
}
customElements.define('my-element', MyElement);
Untuk menggunakan pengontrol, Anda perlu membuat instance pengontrol dengan meneruskan referensi ke host pengontrol (yang merupakan komponen <my-element>
), lalu menggunakan pengontrol di metode render
.
Memicu rendering ulang di pengontrol
Perlu diperhatikan bahwa ini akan menampilkan waktu, tetapi waktu tidak diperbarui. Hal ini karena pengontrol menetapkan tanggal setiap detik, tetapi host tidak memperbarui. Dan juga karena date
berubah pada class ClockController
dan bukan merupakan komponen lagi. Hal ini berarti bahwa setelah date
disetel pada pengontrol, host perlu diberi tahu untuk menjalankan siklus proses pembaruannya dengan host.requestUpdate()
.
// Lit (TS & JS) - clock.ts / clock.js
private tick() {
this.date = new Date();
this.host.requestUpdate();
}
Sekarang jamnya sudah berdenting!
Untuk perbandingan yang lebih mendalam tentang kasus penggunaan umum dengan hook, lihat bagian Topik Lanjutan - Hook.
Di bagian ini, Anda akan mempelajari cara menggunakan slot untuk mengelola turunan di Lit.
Slot & Turunan
Slot mengaktifkan komposisi dengan memungkinkan Anda untuk menumpuk komponen.
Dalam React, turunan diwariskan melalui properti. Slot default-nya adalah props.children
dan fungsi render
menentukan posisi slot default. Contoh:
const MyArticle = (props) => {
return <article>{props.children}</article>;
};
Perlu diingat bahwa props.children
adalah Komponen React dan bukan merupakan elemen HTML.
Di Lit, turunan disusun dalam fungsi render dengan elemen slot. Perlu diperhatikan bahwa turunan tidak diwariskan dengan cara yang sama seperti React. Di Lit, turunan merupakan HTMLElements yang dilampirkan ke slot. Lampiran ini disebut Proyeksi.
@customElement("my-article")
export class MyArticle extends LitElement {
render() {
return html`
<article>
<slot></slot>
</article>
`;
}
}
Beberapa Slot
Di React, menambahkan beberapa slot pada dasarnya sama dengan mewarisi lebih banyak properti.
const MyArticle = (props) => {
return (
<article>
<header>
{props.headerChildren}
</header>
<section>
{props.sectionChildren}
</section>
</article>
);
};
Demikian pula, menambahkan lebih banyak elemen <slot>
akan membuat lebih banyak slot di Lit. Beberapa slot ditentukan dengan atribut name
: <slot name="slot-name">
. Hal ini memungkinkan turunan untuk menyatakan slot mana yang akan ditetapkan.
@customElement("my-article")
export class MyArticle extends LitElement {
render() {
return html`
<article>
<header>
<slot name="headerChildren"></slot>
</header>
<section>
<slot name="sectionChildren"></slot>
</section>
</article>
`;
}
}
Konten Slot Default
Slot akan menampilkan subhierarki jika tidak ada node yang diproyeksikan ke slot tersebut. Jika node diproyeksikan ke slot, slot tersebut tidak akan menampilkan subhierarki dan akan menampilkan node yang diproyeksikan.
@customElement("my-element")
export class MyElement extends LitElement {
render() {
return html`
<section>
<div>
<slot name="slotWithDefault">
<p>
This message will not be rendered when children are attached to this slot!
<p>
</slot>
</div>
</section>
`;
}
}
Menetapkan turunan ke slot
Di React, turunan ditetapkan ke slot melalui properti Komponen. Pada contoh di bawah ini, elemen React diteruskan ke properti headerChildren
dan sectionChildren
.
const MyNewsArticle = () => {
return (
<MyArticle
headerChildren={<h3>Extry, Extry! Read all about it!</h3>}
sectionChildren={<p>Children are props in React!</p>}
/>
);
};
Di Lit, turunan ditetapkan ke slot menggunakan atribut slot
.
@customElement("my-news-article")
export class MyNewsArticle extends LitElement {
render() {
return html`
<my-article>
<h3 slot="headerChildren">
Extry, Extry! Read all about it!
</h3>
<p slot="sectionChildren">
Children are composed with slots in Lit!
</p>
</my-article>
`;
}
}
Jika tidak ada slot default (misalnya <slot>
) dan tidak ada slot yang memiliki atribut name
(misalnya <slot name="foo">
) yang cocok dengan atribut slot
dari turunan elemen kustom (misalnya <div slot="foo">
), node tersebut tidak akan diproyeksikan dan tidak akan ditampilkan.
Terkadang, developer mungkin perlu mengakses API HTMLElement.
Pada bagian ini, Anda akan mempelajari cara memperoleh referensi elemen di Lit.
Referensi React
Komponen React ditranspilasikan menjadi serangkaian panggilan fungsi yang membuat DOM virtual saat dipanggil. DOM virtual ini ditafsirkan oleh ReactDOM dan merender HTMLElements.
Di React, referensi adalah ruang di memori berisi HTMLEding yang dihasilkan.
const RefsExample = (props) => {
const inputRef = React.useRef(null);
const onButtonClick = React.useCallback(() => {
inputRef.current?.focus();
}, [inputRef]);
return (
<div>
<input type={"text"} ref={inputRef} />
<br />
<button onClick={onButtonClick}>
Click to focus on the input above!
</button>
</div>
);
};
Pada contoh di atas, komponen React akan melakukan hal berikut:
- Merender input teks kosong dan tombol dengan teks
- Memfokuskan input saat tombol diklik
Setelah render awal, React akan menetapkan inputRef.current
ke HTMLInputElement
yang dihasilkan melalui atribut ref
.
Lit "Referensi" dengan @query
Lit berada dekat dengan browser dan membuat abstraksi yang sangat tipis pada fitur browser asli.
React yang setara dengan refs
di Lit merupakan HTMLElement yang ditampilkan oleh dekorator @query
dan @queryAll
.
@customElement("my-element")
export class MyElement extends LitElement {
@query('input') // Define the query
inputEl!: HTMLInputElement; // Declare the prop
// Declare the click event listener
onButtonClick() {
// Use the query to focus
this.inputEl.focus();
}
render() {
return html`
<input type="text"></input>
<br />
<!-- Bind the click listener -->
<button @click=${this.onButtonClick}>
Click to focus on the input above!
</button>
`;
}
}
Pada contoh di atas, komponen Lit melakukan:
- Menentukan properti di
MyElement
menggunakan dekorator@query
(membuat pengambil untukHTMLInputElement
). - Mendeklarasikan dan memasang callback peristiwa klik yang disebut
onButtonClick
. - Memfokuskan input pada klik tombol
Dalam JavaScript, dekorasi @query
dan @queryAll
menjalankan querySelector
dan querySelectorAll
secara berturut-turut. JavaScript ini setara dengan @query('input') inputEl!: HTMLInputElement;
get inputEl() {
return this.renderRoot.querySelector('input');
}
Setelah komponen Lit melakukan template metode render
ke root my-element
, dekorator @query
sekarang akan mengizinkan inputEl
untuk menampilkan elemen input
pertama yang ditemukan di root render. null
akan ditampilkan jika @query
tidak dapat menemukan elemen yang ditentukan.
Jika ada beberapa elemen input
di root render, @queryAll
akan menampilkan daftar node.
Di bagian ini, Anda akan mempelajari cara memediasi status antarkomponen di Lit.
Komponen yang Dapat Digunakan Kembali
React meniru pipeline rendering fungsional dengan aliran data turun. Induk akan memberikan status ke turunan melalui prop. Turunan berkomunikasi dengan induk melalui callback yang ditemukan di prop.
const CounterButton = (props) => {
const label = props.step < 0
? `- ${-1 * props.step}`
: `+ ${props.step}`;
return (
<button
onClick={() =>
props.addToCounter(props.step)}>{label}</button>
);
};
Pada contoh di atas, komponen React melakukan beberapa hal berikut:
- Membuat label berdasarkan nilai
props.step
. - Merender tombol dengan +langkah atau -langkah sebagai labelnya
- Memperbarui komponen induk melalui pemanggilan
props.addToCounter
denganprops.step
sebagai argumen saat diklik
Meskipun dapat meneruskan callback di Lit, pola konvensinya akan berbeda. Komponen React pada contoh di atas dapat ditulis sebagai Komponen Lit dalam contoh di bawah:
@customElement('counter-button')
export class CounterButton extends LitElement {
@property({type: Number}) step: number = 0;
onClick() {
const event = new CustomEvent('update-counter', {
bubbles: true,
detail: {
step: this.step,
}
});
this.dispatchEvent(event);
}
render() {
const label = this.step < 0
? `- ${-1 * this.step}` // "- 1"
: `+ ${this.step}`; // "+ 1"
return html`
<button @click=${this.onClick}>${label}</button>
`;
}
}
Pada contoh di atas, Komponen Lit akan melakukan beberapa hal berikut:
- Membuat properti reaktif
step
- Mengirim peristiwa kustom bernama
update-counter
yang membawa nilaistep
elemen saat diklik
Peristiwa browser muncul dari elemen turunan hingga elemen induk. Peristiwa memungkinkan turunan menyiarkan peristiwa interaksi dan perubahan status. React pada dasarnya meneruskan status ke arah yang berbeda sehingga melihat pengiriman Komponen React dan memproses peristiwa dengan cara yang sama seperti Menyalakan Komponen adalah hal yang tidak biasa.
Komponen Stateful
Dalam React, penggunaan hook untuk mengelola status adalah hal yang umum. Komponen MyCounter
dapat dibuat dengan menggunakan kembali Komponen CounterButton
. Perhatikan cara addToCounter
diteruskan ke kedua instance CounterButton
.
const MyCounter = (props) => {
const [counterSum, setCounterSum] = React.useState(0);
const addToCounter = useCallback(
(step) => {
setCounterSum(counterSum + step);
},
[counterSum, setCounterSum]
);
return (
<div>
<h3>Σ: {counterSum}</h3>
<CounterButton
step={-1}
addToCounter={addToCounter} />
<CounterButton
step={1}
addToCounter={addToCounter} />
</div>
);
};
Contoh di atas melakukan beberapa hal berikut:
- Membuat status
count
. - Membuat callback yang menambahkan nomor ke status
count
. CounterButton
menggunakanaddToCounter
untuk memperbaruicount
berdasarkanstep
pada setiap klik.
Penerapan MyCounter
yang serupa dapat dilakukan di Lit. Perhatikan cara addToCounter
tidak diteruskan ke counter-button
. Sebaliknya, callback di-binding sebagai pemroses peristiwa ke peristiwa @update-counter
pada elemen induk.
@customElement("my-counter")
export class MyCounter extends LitElement {
@property({type: Number}) count = 0;
addToCounter(e: CustomEvent<{step: number}>) {
// Get step from detail of event or via @query
this.count += e.detail.step;
}
render() {
return html`
<div @update-counter="${this.addToCounter}">
<h3>Σ ${this.count}</h3>
<counter-button step="-1"></counter-button>
<counter-button step="1"></counter-button>
</div>
`;
}
}
Contoh di atas melakukan beberapa hal berikut:
- Membuat properti reaktif bernama
count
yang akan mengupdate komponen saat nilai diubah - Mem-binding callback
addToCounter
ke pemroses peristiwa@update-counter
- Mengupdate
count
dengan menambahkan nilai yang ditemukan didetail.step
peristiwaupdate-counter
- Menetapkan nilai
step
counter-button
melalui atributstep
Lebih konvensional menggunakan properti reaktif di Lit untuk menyiarkan perubahan dari induk ke turunan. Demikian pula, sebaiknya gunakan sistem peristiwa browser untuk menampilkan detail dari bawah ke atas.
Pendekatan ini mengikuti praktik terbaik dan mematuhi sasaran Lit dalam memberikan dukungan lintas platform untuk komponen web.
Di bagian ini, Anda akan mempelajari penataan gaya di Lit.
Penggayaan
Lit menawarkan beberapa cara untuk menata gaya elemen serta solusi bawaan.
Gaya Inline
Lit mendukung gaya inline serta mem-binding-nya.
import {LitElement, html, css} from 'lit';
import {customElement} from 'lit/decorators.js';
@customElement('my-element')
class MyElement extends LitElement {
render() {
return html`
<div>
<h1 style="color:orange;">This text is orange</h1>
<h1 style="color:rebeccapurple;">This text is rebeccapurple</h1>
</div>
`;
}
}
Pada contoh di atas, ada 2 judul masing-masing dengan gaya inline.
Sekarang coba mem-binding border: 1px solid black
ke teks berwarna oranye:
<h1 style="color:orange;${'border: 1px solid black;'}">This text is orange</h1>
Mengharuskan penghitungan string gaya setiap waktu mungkin agak mengganggu sehingga Lit menawarkan perintah untuk mempermudah hal ini.
styleMap
Perintah styleMap
mempermudah penggunaan JavaScript untuk menetapkan gaya inline. Contoh:
import {LitElement, html, css} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import {styleMap} from 'lit/directives/style-map';
@customElement('my-element')
class MyElement extends LitElement {
@property({type: String})
color = '#000'
render() {
// Define the styleMap
const headerStyle = styleMap({
'border-color': this.color,
});
return html`
<div>
<h1
style="border-style:solid;
<!-- Use the styleMap -->
border-width:2px;${headerStyle}">
This div has a border color of ${this.color}
</h1>
<input
type="color"
@input=${e => (this.color = e.target.value)}
value="#000">
</div>
`;
}
}
Contoh di atas melakukan beberapa hal berikut:
- Menampilkan
h1
dengan batas dan pemilih warna - Mengubah
border-color
menjadi nilai dari pemilih warna
Selain itu, ada styleMap
yang digunakan untuk mengatur gaya h1
. styleMap
mengikuti sintaksis yang mirip dengan sintaksis binding atribut style
React.
CSSResult
Cara yang disarankan untuk menentukan gaya komponen ialah menggunakan literal template yang diberi tag css
.
import {LitElement, html, css} from 'lit';
import {customElement} from 'lit/decorators.js';
const ORANGE = css`orange`;
@customElement('my-element')
class MyElement extends LitElement {
static styles = [
css`
#orange {
color: ${ORANGE};
}
#purple {
color: rebeccapurple;
}
`
];
render() {
return html`
<div>
<h1 id="orange">This text is orange</h1>
<h1 id="purple">This text is rebeccapurple</h1>
</div>
`;
}
}
Contoh di atas melakukan beberapa hal berikut:
- Mendeklarasikan literal template tag CSS dengan binding
- Menetapkan warna dari dua
h1
dengan ID
Manfaat menggunakan tag template css
:
- Diurai sekali per class vs per instance
- Diterapkan dengan mempertimbangkan penggunaan kembali modul
- Dapat dengan mudah memisahkan gaya ke dalam filenya sendiri
- Kompatibel dengan polyfill Properti Khusus CSS
Selain itu, perhatikan tag <style>
di index.html
:
<!-- index.html -->
<style>
h1 {
color: red !important;
}
</style>
Lit akan mengatur gaya komponen Anda ke root-nya. Ini berarti gaya tidak akan bocor. Untuk meneruskan gaya ke komponen, tim Lit menyarankan untuk menggunakan Properti Khusus CSS karena dapat memasuki cakupan gaya Lit.
Tag Gaya
Anda juga dapat membuat tag <style>
inline di template Anda. Browser akan menghapus duplikat tag gaya ini. Namun, dengan menempatkannya dalam template Anda, tag akan diurai per instance komponen dan bukan per class seperti pada kasus template yang diberi tag css
. Selain itu, penghapusan duplikat browser CSSResult
jauh lebih cepat.
Tag Link
Penggunaan <link rel="stylesheet">
di template Anda juga kemungkinan adanya gaya, tetapi ini juga tidak direkomendasikan karena dapat menyebabkan flash awal pada konten yang tidak bergaya (FOUC).
JSX & Pembuatan Template
Lit & DOM Virtual
Lit-html tidak menyertakan DOM Virtual biasa yang membedakan setiap node individual. Sebagai gantinya, aplikasi ini menggunakan fitur performa yang bersifat intrinsik pada spesifikasi yang diberi tag literal template dari ES2015. Literal template yang diberi tag merupakan string literal template dengan fungsi tag yang disertakan.
Berikut adalah contoh literal template:
const str = 'string';
console.log(`This is a template literal ${str}`);
Berikut adalah contoh literal template yang diberi tag:
const tag = (strings, ...values) => ({strings, values});
const f = (x) => tag`hello ${x} how are you`;
console.log(f('world')); // {strings: ["hello ", " how are you"], values: ["world"]}
console.log(f('world').strings === f(1 + 2).strings); // true
Pada contoh di atas, tagnya adalah fungsi tag
dan fungsi f
menampilkan pemanggilan literal template yang diberi tag.
Banyak keajaiban performa di Lit berasal dari fakta bahwa array string yang diteruskan ke fungsi tag memiliki penunjuk yang sama (seperti yang ditunjukkan pada console.log
kedua). Browser tidak membuat ulang array strings
baru di setiap pemanggilan fungsi tag karena menggunakan literal template yang sama (yaitu di lokasi yang sama di AST). Jadi, mem-binding, mengurai, dan men-cache template Lit dapat memanfaatkan fitur ini tanpa memerlukan banyak biaya overhead diffing runtime.
Perilaku browser bawaan dari literal template yang diberi tag ini memberikan keunggulan performa bagi Lit. Sebagian besar DOM Virtual konvensional melakukan mayoritas pekerjaan di JavaScript. Namun, literal template yang diberi tag dapat melakukan sebagian besar diffing di C++ browser.
Jika Anda ingin mulai menggunakan literal template yang diberi tag HTML dengan React atau Preact, tim Lit akan merekomendasikan htm
library.
Namun, seperti halnya situs Google Codelabs dan beberapa editor kode online, Anda akan melihat bahwa penyorotan sintaksis untuk literal template yang diberi tag bukan hal yang umum. Beberapa IDE dan editor teks mendukungnya secara default, seperti Atom dan penyorot kode GitHub. Tim Lit juga bekerja sangat erat dengan komunitas untuk mengelola project seperti lit-plugin
yang merupakan plugin VS Code yang akan menambahkan penyorotan sintaksis, pemeriksaan jenis, dan intellisense ke project Lit Anda.
Lit & JSX + React DOM
JSX tidak berjalan di browser dan sebagai gantinya gunakan praprosesor untuk mengonversi panggilan JSX ke panggilan fungsi JavaScript (biasanya melalui Babel).
Misalnya, Babel akan mengubah ini:
const element = <div className="title">Hello World!</div>;
ReactDOM.render(element, mountNode);
menjadi ini:
const element = React.createElement('div', {className: 'title'}, 'Hello World!');
ReactDOM.render(element, mountNode);
React DOM kemudian mengambil output React dan menerjemahkannya ke DOM yang sebenarnya – properti, atribut, pemroses peristiwa, dan semua.
Lit-html menggunakan literal template bertag yang dapat berjalan di browser tanpa transpilasi atau praprosesor. Ini artinya untuk mulai menggunakan Lit, Anda hanya memerlukan file HTML, skrip modul ES, dan server. Berikut ini skrip yang sepenuhnya dapat dijalankan dengan browser:
<!DOCTYPE html>
<html>
<head>
<script type="module">
import {html, render} from 'https://cdn.skypack.dev/lit';
render(
html`<div>Hello World!</div>`,
document.querySelector('.root')
)
</script>
</head>
<body>
<div class="root"></div>
</body>
</html>
Selain itu, karena sistem template Lit adalah lit-html dan tidak menggunakan DOM Virtual kovensional, melainkan menggunakan API DOM secara langsung, ukuran Lit 2 di bawah 5 kb yang telah diperkecil dan di-gzip dibandingkan dengan React (2,8 kb) + reaksi-dom (39,4 kb) 40kb yang telah diperkecil dan di-gizip juga.
Peristiwa
React menggunakan sistem peristiwa sintetis. Ini artinya react-dom harus mendefinisikan setiap peristiwa yang akan digunakan di setiap komponen dan menyediakan pemroses peristiwa camelCase yang setara untuk setiap jenis node. Oleh karena itu, JSX tidak memiliki metode untuk menentukan pemroses peristiwa untuk peristiwa kustom dan developer harus menggunakan ref
, lalu menerapkan pemroses secara imperatif. Hal ini akan menghasilkan pengalaman developer sub-par saat mengintegrasikan library yang tidak memiliki React sehingga harus menulis wrapper khusus React.
Lit-html mengakses DOM secara langsung dan menggunakan peristiwa native sehingga penambahan pemroses peristiwa menjadi semudah @event-name=${eventNameListener}
. Ini berarti penguraian runtime yang lebih sedikit dilakukan untuk menambahkan pemroses peristiwa serta mengaktifkan peristiwa.
Komponen & Properti
Komponen React & elemen khusus
Di balik layar, LitEement menggunakan elemen kustom untuk mengemas komponennya. Elemen kustom memasukkan beberapa keseimbangan antara komponen React saat berhubungan dengan komponentasi (status dan siklus proses dibahas lebih lanjut di bagian Status & Siklus Proses).
Beberapa keunggulan yang dimiliki Elemen Kustom sebagai sistem komponen:
- Native untuk browser dan tidak memerlukan alat apa pun
- Cocok dengan semua API browser dari
innerHTML
dandocument.createElement
hinggaquerySelector
- Biasanya dapat digunakan di seluruh framework
- Dapat dengan santai didaftarkan dengan
customElements.define
dan DOM "hydrate"
Beberapa kerugian yang dimiliki Elemen Kustom dibandingkan dengan komponen React:
- Tidak dapat membuat elemen khusus tanpa menetapkan class (tidak ada komponen fungsional seperti JSX)
- Harus berisi tag penutup
- Catatan: meskipun memiliki kemudahan developer, vendor browser cenderung tidak menyukai spesifikasi tag yang menutup sendiri, itulah sebabnya spesifikasi yang lebih baru cenderung tidak menyertakan tag yang menutup sendiri
- Mendaftarkan node tambahan ke hierarki DOM yang dapat menyebabkan masalah tata letak
- Harus terdaftar melalui JavaScript
Lit telah berjalan dengan elemen kustom di atas sistem elemen yang dipesan terlebih dahulu karena elemen kustom ditanamkan ke dalam browser, dan tim Lit percaya bahwa manfaat lintas-framework lebih besar daripada manfaat yang diberikan oleh lapisan abstraksi komponen. Bahkan, upaya tim Lit di ruang lit-ssr telah mengatasi masalah utama pada pendaftaran JavaScript. Selain itu, beberapa perusahaan seperti GitHub memanfaatkan pendaftaran lazy element kustom untuk secara progresif meningkatkan halaman dengan hiasan opsional.
Meneruskan data ke elemen kustom
Kesalahpahaman yang umum terjadi pada elemen kustom adalah data hanya dapat diteruskan sebagai string. Kesalahpahaman ini mungkin berasal dari fakta bahwa atribut elemen hanya dapat ditulis sebagai string. Meskipun benar bahwa Lit akan mentransmisikan atribut string ke jenis yang ditentukan, elemen kustom juga dapat menerima data yang kompleks sebagai properti.
Misalnya – dengan definisi LitElement berikut:
// data-test.ts
import {LitElement, html} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('data-test')
class DataTest extends LitElement {
@property({type: Number})
num = 0;
@property({attribute: false})
data = {a: 0, b: null, c: [html`<div>hello</div>`, html`<div>world</div>`]}
render() {
return html`
<div>num + 1 = ${this.num + 1}</div>
<div>data.a = ${this.data.a}</div>
<div>data.b = ${this.data.b}</div>
<div>data.c = ${this.data.c}</div>`;
}
}
num
properti reaktif primitif ditentukan dan akan mengonversi nilai string atribut menjadi number
, kemudian struktur data kompleks dimasukkan dengan attribute:false
yang menonaktifkan penanganan atribut Lit.
Berikut adalah cara meneruskan data ke elemen khusus ini:
<head>
<script type="module">
import './data-test.js'; // loads element definition
import {html} from './data-test.js';
const el = document.querySelector('data-test');
el.data = {
a: 5,
b: null,
c: [html`<div>foo</div>`,html`<div>bar</div>`]
};
</script>
</head>
<body>
<data-test num="5"></data-test>
</body>
Status & Siklus Proses
Callback Siklus Proses React Lainnya
static getDerivedStateFromProps
Tidak ada padanan yang setara di Lit karena properti dan status merupakan properti class yang sama
shouldComponentUpdate
- Lit setara adalah
shouldUpdate
- Dipanggil pada render pertama tidak seperti React
- Serupa dengan fungsi untuk
shouldComponentUpdate
React
getSnapshotBeforeUpdate
Di Lit, getSnapshotBeforeUpdate
serupa dengan update
dan willUpdate
willUpdate
- Dipanggil sebelum
update
- Tidak seperti
getSnapshotBeforeUpdate
,willUpdate
dipanggil sebelumrender
- Perubahan pada properti reaktif di
willUpdate
tidak memicu kembali siklus update - Tempat yang baik untuk menghitung nilai properti yang bergantung pada properti lainnya dan digunakan pada proses update lainnya
- Metode ini dipanggil di server dalam SSR sehingga Anda tidak disarankan mengakses DOM di sini
update
- Dipanggil setelah
willUpdate
- Tidak seperti
getSnapshotBeforeUpdate
,update
dipanggil sebelumrender
- Perubahan pada properti reaktif di
update
tidak akan memicu kembali siklus update jika diubah sebelum memanggilsuper.update
- Tempat yang baik untuk memperoleh informasi dari DOM yang mengelilingi komponen sebelum output yang dirender diserahkan ke DOM
- Metode ini tidak dipanggil di server dalam SSR
Callback Siklus Proses Lit Lainnya
Ada beberapa callback siklus proses yang tidak disebutkan di bagian sebelumnya karena tidak ada yang sama dengannya di dalam React. Yaitu:
attributeChangedCallback
Callback ini dipanggil jika salah satu observedAttributes
elemen berubah. Baik observedAttributes
maupun attributeChangedCallback
merupakan bagian dari spesifikasi elemen khusus dan diimplementasikan oleh Lit di bawah hood guna menyediakan API atribut untuk elemen Lit.
adoptedCallback
Dipanggil ketika komponen dipindahkan ke dokumen baru, misalnya dari documentFragment
HTMLTemplateElement
ke document
utama. Callback ini juga merupakan bagian dari spesifikasi elemen kustom dan hanya dapat digunakan untuk kasus penggunaan lanjutan saat komponen mengubah dokumen.
Metode dan properti siklus proses lainnya
Metode dan properti ini merupakan anggota class yang dapat Anda panggil, ganti, atau tunggu untuk membantu memanipulasi proses siklus proses.
updateComplete
Ini adalah Promise
yang me-resolve saat elemen selesai diupdate karena update dan siklus proses render bersifat asinkron. Misalnya:
async nextButtonClicked() {
this.step++;
// Wait for the next "step" state to render
await this.updateComplete;
this.dispatchEvent(new Event('step-rendered'));
}
getUpdateComplete
Ini adalah metode yang harus diganti untuk menyesuaikan saat updateComplete
di-resolve. Hal ini umum terjadi ketika komponen merender komponen turunan dan siklus render-nya harus sinkron. Misalnya
class MyElement extends LitElement {
...
async getUpdateComplete() {
await super.getUpdateComplete();
await this.myChild.updateComplete;
}
}
performUpdate
Metode ini adalah apa yang memanggil callback siklus proses update. Hal ini umumnya tidak diperlukan kecuali untuk kasus langka saat update harus dilakukan secara sinkron atau untuk penjadwalan khusus.
hasUpdated
Properti ini adalah true
jika komponen telah diupdate setidaknya satu kali.
isConnected
Bagian dari spesifikasi elemen kustom, properti ini akan menjadi true
jika elemen saat ini disematkan ke hierarki dokumen utama.
Visualisasi Siklus Proses Update Lit
Ada 3 bagian dari siklus proses update:
- Pra-update
- Update
- Pasca-update
Pra-Update
Setelah requestUpdate
, update terjadwal akan menunggu.
Update
Pasca-Update
Hook
Mengapa hook
Hook dimasukkan ke dalam React untuk kasus penggunaan komponen fungsi sederhana yang memerlukan status. Dalam banyak kasus, komponen fungsi dengan hook cenderung lebih sederhana dan lebih mudah dibaca dibandingkan dengan komponen class. Meskipun, saat memperkenalkan update status asinkron serta meneruskan data antar-hook atau antar-efek, pola hook biasanya tidak akan cukup, dan solusi berbasis class seperti pengontrol reaktif cenderung terlihat.
Pengontrol & hook permintaan API
Menulis hook yang meminta data dari API merupakan hal umum. Misalnya, ambil komponen fungsi React ini yang melakukan beberapa hal berikut:
index.tsx
- Merender teks
- Merender respons
useAPI
- ID Pengguna + Nama pengguna
- Pesan error
- 404 saat menjangkau pengguna 11 (berdasarkan desain)
- Batalkan error jika pengambilan API dibatalkan
- Memuat Pesan
- Merender tombol tindakan
- Pengguna berikutnya: yang mengambil API untuk pengguna berikutnya
- Batalkan: yang membatalkan pengambilan API dan menampilkan error
useApi.tsx
- Menentukan hook kustom
useApi
- Akan mengambil objek pengguna dari api secara asinkron
- Memancarkan:
- Nama pengguna
- Baik pengambilan sedang dimuat maupun tidak
- Pesan error apa pun
- Callback untuk membatalkan pengambilan
- Pembatalan pengambilan sedang berlangsung jika dibatalkan
- Menentukan hook kustom
Berikut adalah implementasi Lit + Pengontrol Reaktif.
Hasil:
- Pengontrol Reaktif paling mirip dengan hook kustom
- Meneruskan data yang tidak dapat dirender antara callback dan efek
- React menggunakan
useRef
untuk meneruskan data antarauseEffect
danuseCallback
- Lit menggunakan properti class pribadi
- React pada dasarnya meniru perilaku properti class pribadi
- React menggunakan
Turunan
Slot Default
Jika elemen HTML tidak diberi atribut slot
, elemen tersebut akan ditetapkan ke slot tanpa nama default. Pada contoh di bawah, MyApp
akan memasukkan satu paragraf ke dalam slot bernama. Paragraf lainnya akan ditetapkan secara default ke slot tanpa nama".
@customElement("my-element")
export class MyElement extends LitElement {
render() {
return html`
<section>
<div>
<slot></slot>
</div>
<div>
<slot name="custom-slot"></slot>
</div>
</section>
`;
}
}
@customElement("my-app")
export class MyApp extends LitElement {
render() {
return html`
<my-element>
<p slot="custom-slot">
This paragraph will be placed in the custom-slot!
</p>
<p>
This paragraph will be placed in the unnamed default slot!
</p>
</my-element>
`;
}
}
Update Slot
Saat struktur turunan slot berubah, peristiwa slotchange
akan diaktifkan. Komponen Lit dapat mem-binding pemroses peristiwa ke peristiwa slotchange
. Pada contoh di bawah, slot pertama yang ditemukan di shadowRoot
akan menetapkan assignedNodes yang dicatat ke konsol di slotchange
.
@customElement("my-element")
export class MyElement extends LitElement {
onSlotChange(e: Event) {
const slot = this.shadowRoot.querySelector('slot');
console.log(slot.assignedNodes({flatten: true}));
}
render() {
return html`
<section>
<div>
<slot @slotchange="{this.onSlotChange}"></slot>
</div>
</section>
`;
}
}
Referensi
Pembuatan referensi
Lit dan React akan mengekspos referensi ke HTMLElement setelah fungsi render
dipanggil. Namun perlu ditinjau cara React dan Lit menyusun DOM yang nantinya dikembalikan melalui dekorator @query
Lit atau Referensi React.
React adalah pipeline fungsional yang membuat Komponen React, bukan HTMLElement. Ruang di memori dialokasikan karena Ref dideklarasikan sebelum HTMLElement dirender. Inilah alasan Anda melihat null
sebagai nilai awal referensi karena elemen DOM yang sebenarnya belum dibuat (atau dirender), yaitu useRef(null)
.
Setelah ReactDOM mengubah Komponen React menjadi HTMLElement, atribut tersebut akan mencari atribut yang dipanggil ref
di ReactComponent. Jika tersedia, ReactDOM menempatkan referensi HTMLElement ke ref.current
.
LitEement menggunakan fungsi tag template html
dari lit-html untuk membuat Elemen Template di balik layar. LitElement menempatkan konten template ke DOM bayangan elemen kustom setelah dirender. DOM bayangan adalah hierarki DOM tercakup yang dienkapsulasi oleh root bayangan. Dekorator @query
kemudian membuat pengambil untuk properti yang pada dasarnya menjalankan this.shadowRoot.querySelector
pada root tercakup.
Elemen Beberapa Kueri
Pada contoh di bawah ini, dekorator @queryAll
akan menampilkan dua paragraf dalam root bayangan sebagai NodeList
.
@customElement("my-element")
export class MyElement extends LitElement {
@queryAll('p')
paragraphs!: NodeList;
render() {
return html`
<p>Hello, world!</p>
<p>How are you?</p>
`;
}
}
Pada dasarnya, @queryAll
membuat pengambil untuk paragraphs
yang menampilkan hasil this.shadowRoot.querySelectorAll()
. Di JavaScript, pengambil dapat dideklarasikan untuk melakukan tujuan yang sama:
get paragraphs() {
return this.renderRoot.querySelectorAll('p');
}
Elemen Perubahan Kueri
Dekorator @queryAsync
lebih cocok untuk menangani node yang dapat berubah berdasarkan status properti elemen lain.
Pada contoh di bawah, @queryAsync
akan menemukan elemen paragraf pertama. Namun, elemen paragraf hanya akan dirender jika renderParagraph
menghasilkan angka ganjil secara acak. Perintah @queryAsync
akan menampilkan janji yang akan diselesaikan saat paragraf pertama tersedia.
@customElement("my-dissappearing-paragraph")
export class MyDisapppearingParagraph extends LitElement {
@queryAsync('p')
paragraph!: Promise<HTMLElement>;
renderParagraph() {
const randomNumber = Math.floor(Math.random() * 10)
if (randomNumber % 2 === 0) {
return "";
}
return html`<p>This checkbox is checked!`
}
render() {
return html`
${this.renderParagraph()}
`;
}
}
Status Mediasi
Dalam React, pengertian konvensi adalah menggunakan callback karena status dimediasi oleh React itu sendiri. React melakukan yang terbaik untuk tidak bergantung pada status yang disediakan oleh elemen. DOM hanyalah efek dari proses rendering.
Status Eksternal
Anda dapat menggunakan Redux, MobX, atau library pengelolaan status lainnya bersama Lit.
Komponen Lit dibuat dalam cakupan browser. Jadi, library yang juga ada dalam cakupan browser tersedia untuk Lit. Banyak library mengagumkan yang di-build agar memanfaatkan sistem pengelolaan status yang ada di Lit.
Berikut adalah seri oleh Vaadin yang menjelaskan cara memanfaatkan Redux dalam komponen Lit.
Lihat lit-mobx dari Adobe untuk melihat cara situs berskala besar memanfaatkan MobX di Lit.
Sebagai tambahan, lihat Elemen Apollo untuk mengetahui cara developer memasukkan GraphQL dalam komponen web mereka.
Lit berfungsi dengan fitur browser asli dan sebagian besar solusi pengelolaan status dalam cakupan browser dapat digunakan dalam komponen Lit.
Penggayaan
DOM Bayangan
Untuk merangkum gaya dan DOM secara native dalam Elemen Khusus, Lit menggunakan DOM Bayangan. Root Bayangan membuat pohon bayangan yang terpisah dari pohon dokumen utama. Ini berarti bahwa sebagian besar gaya disertakan dalam dokumen ini. Gaya tertentu mengalami kebocoran seperti warna, dan gaya terkait font lainnya.
Bayangan DOM juga memperkenalkan konsep dan pemilih baru pada spesifikasi CSS:
:host, :host(:hover), :host([hover]) {
/* Styles the element in which the shadow root is attached to */
}
slot[name="title"]::slotted(*), slot::slotted(:hover), slot::slotted([hover]) {
/*
* Styles the elements projected into a slot element. NOTE: the spec only allows
* styling the direcly slotted elements. Children of those elements are not stylable.
*/
}
Berbagi Gaya
Lit memudahkan Anda berbagi gaya antar-komponen dalam bentuk CSSTemplateResults
melalui tag template css
. Contoh:
// typography.ts
export const body1 = css`
.body1 {
...
}
`;
// my-el.ts
import {body1} from './typography.ts';
@customElement('my-el')
class MyEl Extends {
static get styles = [
body1,
css`/* local styles come after so they will override bod1 */`
]
render() {
return html`<div class="body1">...</div>`
}
}
Tema
Akar bayangan memberikan sedikit tantangan untuk tema konvensional yang biasanya merupakan pendekatan tag gaya top-down. Cara konvensional untuk menanganinya dengan Komponen Web yang menggunakan DOM bayangan adalah mengekspos API gaya melalui Properti Khusus CSS. Misalnya, ini adalah pola yang digunakan Desain Material:
.mdc-textfield-outline {
border-color: var(--mdc-theme-primary, /* default value */ #...);
}
.mdc-textfield--input {
caret-color: var(--mdc-theme-primary, #...);
}
Pengguna tersebut kemudian akan mengubah tema situs dengan menerapkan nilai properti khusus:
html {
--mdc-theme-primary: #F00;
}
html[dark] {
--mdc-theme-primary: #F88;
}
Jika tema top-down adalah tema wajib dan Anda tidak dapat mengekspos gaya, Anda dapat menonaktifkan DOM Bayangan dengan mengganti createRenderRoot
agar menampilkan this
yang kemudian merender template komponen ke elemen khusus itu sendiri daripada ke akar bayangan yang disematkan ke elemen khusus. Dengan ini Anda akan kehilangan: enkapsulasi gaya, enkapsulasi DOM, dan slot.
Produksi
IE 11
Jika harus mendukung browser lama seperti IE 11, Anda harus memuat beberapa polyfill yang mencakup 33 kb lainnya. Informasi selengkapnya dapat ditemukan di sini.
Paket Bersyarat
Tim Lit merekomendasikan Anda untuk menyajikan dua paket yang berbeda, satu untuk IE 11 dan satu untuk browser modern. Ada beberapa manfaatnya:
- Melayani ES 6 lebih cepat dan akan melayani sebagian besar klien Anda
- ES 5 yang ditranspilasikan akan meningkatkan ukuran paket secara signifikan
- Paket bersyarat memberi Anda dengan semua yang terbaik dari kedua dunia
- Dukungan IE 11
- Tidak ada pelambatan di browser modern
Info selengkapnya mengenai cara membuat paket yang dilayani secara bersyarat dapat ditemukan di situs dokumentasi kami di sini.