Lit框架:基于Web Components的輕量級前端開發(fā)方案
在前端JavaScript框架生態(tài)中,Lit為響應式編程提供了一個獨特選擇。雖然開發(fā)者對它的興趣持續(xù)增長,但相比其他主流響應式框架,它仍保持著低調(diào)姿態(tài)。Lit基于Mozilla的Web Components標準構(gòu)建,專注于提供極致的運行效率和精簡的核心功能集。
Web Components標準解析
要理解Lit,必須先了解Web Components。這個被所有主流瀏覽器支持的標準,為定義UI組件提供了一致性方案。Web Components的核心理念是為開發(fā)者提供瀏覽器原生工具來處理UI組件的通用需求。理想情況下,無論是React、Vue還是其他框架,都應該構(gòu)建在Web Components層之上,從而提升Web開發(fā)的一致性。
Lit通過簡化Web Components的開發(fā)體驗,打造了一個簡潔高效的類庫。它生成的Web Components本質(zhì)上就是自定義HTML元素,這些元素具有廣泛適用性,例如可以在React中使用。以下是基于標準構(gòu)建的簡單問候組件:
class SimpleGreetingextendsHTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const name = this.getAttribute('name') || 'World';
this.shadowRoot.innerHTML = `
<style>
p {
color: navy;
font-family: sans-serif;
border: 1px solid lightblue;
padding: 5px;
display: inline-block;
}
</style>
<p>Hello, ${name}!</p>
`;
}
}這個組件根據(jù)name屬性輸出問候語,并帶有組件作用域內(nèi)的簡單樣式。在瀏覽器控制臺(F12)中輸入以下代碼即可使用:
const defaultGreeting = document.createElement('simple-greeting');
document.body.appendChild(defaultGreeting);雖然構(gòu)造器和shadowRoot等特性值得關注,但最核心的是Web Components允許你使用瀏覽器標準定義封裝功能,這些代碼可以直接在瀏覽器控制臺運行。
使用Lit開發(fā)Web Components
現(xiàn)在讓我們用Lit實現(xiàn)相同功能:
import { LitElement, html, css } from'lit';
import { customElement, property } from'lit/decorators.js';
@customElement('simple-greeting-lit')
exportclassSimpleGreetingLitextendsLitElement {
@property({ type: String })
name = 'World'; // 默認值
static styles = css`
p {
color: blueviolet;
font-family: sans-serif;
border: 2px solid mediumpurple;
padding: 8px;
display: inline-block;
}
span {
font-weight: bold;
}
`;
render() {
return html`<p>Hello, <span>${this.name}</span>! This is Lit.</p>`;
}
}這段代碼實現(xiàn)了與Web Components示例相同的功能,但代碼量和復雜度顯著降低。以@開頭的裝飾器(又稱注解)讓我們能簡潔地聲明customElement和name屬性。借助Lit的css函數(shù)(一個標簽模板字面量函數(shù)),我們不再需要默認構(gòu)造器,也不再需要內(nèi)聯(lián)CSS標記。
Lit還允許我們使用render方法返回由html函數(shù)生成的模板。html函數(shù)參數(shù)的內(nèi)容可以結(jié)合HTML與變量插值,類似于JSX和其他模板語法,但注意我們使用${}而非{},并且使用this來引用組件。
最簡單的方式是通過Lit在線演練場嘗試。注意在演練場中需要切換TS(TypeScript)選項才能使注解生效(這個限制僅適用于演練場,構(gòu)建時注解在JavaScript中同樣有效)。
為Lit組件添加響應式特性
現(xiàn)在讓我們實現(xiàn)更高級的響應式功能,使Lit的name變量可交互。我們將添加一個輸入框來實現(xiàn)雙向綁定:
render() {
return html`
<div>
<input .value=${this.name} @input=${this._handleNameInput}>
<p>Hello, <span>${this.name}</span>!</p>
</div>
`;
}
_handleNameInput(event: Event) {
const inputElement = event.target as HTMLInputElement;
this.name = inputElement.value;
}這段代碼在原有功能基礎上增加了input元素和事件處理函數(shù)。輸入框使用標準HTML文本類型,其value屬性前綴的.操作符是Lit實現(xiàn)雙向綁定的關鍵——它告訴Lit需要綁定的是JavaScript動態(tài)屬性而非靜態(tài)值。@input屬性將變更處理器指向我們的_handleNameInput函數(shù),該函數(shù)使用標準DOM操作獲取輸入值并賦值給this.name變量,完成了雙向綁定的另一側(cè)。
使用Lit組件內(nèi)部狀態(tài)
響應式庫的另一個核心特性是組件內(nèi)部狀態(tài)管理。Lit同樣簡化了這個方面。例如要實現(xiàn)顯示/隱藏功能:
@state()
private _showSecretMessage = false;然后在模板中使用這個狀態(tài)變量:
${this._showSecretMessage
? html`<p>This is the secret message!</p>`
: '' /* 為false時不渲染內(nèi)容 */
}這段模板代碼使用JavaScript三元運算符實現(xiàn)條件渲染。要切換狀態(tài)值,可以添加按鈕:
<button @click=${this._toggleSecretMessage}>
${this._showSecretMessage ? 'Hide' : 'Show'} Secret
</button>對應的點擊處理器非常簡單:
_toggleSecretMessage() {
this._showSecretMessage = !this._showSecretMessage;
}在Lit中渲染集合
讓我們看看Lit如何處理集合渲染。首先創(chuàng)建Hobbits屬性:
@property({ type: Array })
hobbits = ["Frodo Baggins", "Samwise Gamgee", "Merry Brandybuck", "Pippin Took"];然后展示這些霍比特人:
<p>The Fellowship's Hobbits:</p>
${this.hobbits && this.hobbits.length > 0
? html`
<ul>
${this.hobbits.map(
(hobbitName) => html`<li>${hobbitName}</li>`
)}
</ul>
`
: html`<p>(No hobbits listed in this roster!)</p>`
}我們再次使用三元運算符處理空列表情況,對非空列表則使用map函數(shù)遍歷輸出每個列表項。
使用Lit進行API調(diào)用
現(xiàn)在讓我們從Middle Earth轉(zhuǎn)向Westeros,從遠程API加載角色數(shù)據(jù)。首先創(chuàng)建內(nèi)部state變量管理fetch promise:
@state()
private _characterDataPromise: Promise<unknown>;在構(gòu)造器中初始化數(shù)據(jù)加載:
constructor() {
super();
this._characterDataPromise = this._fetchCharacterData();
}_fetchCharacterData函數(shù)實現(xiàn)如下:
private async _fetchCharacterData() {
const apiUrl = "https://www.anapioficeandfire.com/api/characters?page=1&pageSize=10";
try {
const response = awaitfetch(apiUrl);
if (!response.ok) {
thrownewError(`API request failed with status: ${response.status}`);
}
constjson: Array<{ name: string, culture: string, born: string, aliases: string[] }> = await response.json();
if (json && json.length > 0) {
const characterTemplates = json.map((char) => {
const displayName = char.name || (char.aliases && char.aliases[0]) || "Unnamed Character";
return html`
<div class="character-card">
<h3>${displayName}</h3>
${char.culture ? html`<p> - Culture: ${char.culture}</p>` : ''}
${char.born ? html`<p>, Born: ${char.born}</p>` : ''}
</div>
`;
});
return html`${characterTemplates}`;
} else {
return html`<p>No characters found in these lands!</p>`;
}
} catch (error) {
console.error("Failed to fetch Game of Thrones character data:", error);
returnPromise.resolve(html`<p>Could not summon characters: ${error.message}</p>`);
}
}在主模板中等待promise結(jié)果:
return html`
<div>
<h2>Characters from the Seven Kingdoms (or thereabouts):</h2>
${until(
this._characterDataPromise,
html`<p>Sending a raven for news (loading characters...).</p>`
)}
</div>
`;總結(jié)
Lit蘊含了許多創(chuàng)新理念,其基于Web Components標準的特性使其受歡迎程度并不令人意外。關鍵問題在于Lit是否能成為React、Svelte和Vue等框架通用的組件系統(tǒng)。如果實現(xiàn)這一點,它將進入全新的發(fā)展階段。就目前而言,Lit本身就是一個可行的方案,特別適合重視標準合規(guī)性的項目。
本文所有示例的源代碼可以在我的GitHub倉庫中找到。
原文地址:https://www.infoworld.com/article/2334669/intro-to-lit-a-javascript-framework.html





























