Lit框架:基于Web Components的輕量級前端開發方案
在前端JavaScript框架生態中,Lit為響應式編程提供了一個獨特選擇。雖然開發者對它的興趣持續增長,但相比其他主流響應式框架,它仍保持著低調姿態。Lit基于Mozilla的Web Components標準構建,專注于提供極致的運行效率和精簡的核心功能集。
Web Components標準解析
要理解Lit,必須先了解Web Components。這個被所有主流瀏覽器支持的標準,為定義UI組件提供了一致性方案。Web Components的核心理念是為開發者提供瀏覽器原生工具來處理UI組件的通用需求。理想情況下,無論是React、Vue還是其他框架,都應該構建在Web Components層之上,從而提升Web開發的一致性。
Lit通過簡化Web Components的開發體驗,打造了一個簡潔高效的類庫。它生成的Web Components本質上就是自定義HTML元素,這些元素具有廣泛適用性,例如可以在React中使用。以下是基于標準構建的簡單問候組件:
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>
`;
}
}這個組件根據name屬性輸出問候語,并帶有組件作用域內的簡單樣式。在瀏覽器控制臺(F12)中輸入以下代碼即可使用:
const defaultGreeting = document.createElement('simple-greeting');
document.body.appendChild(defaultGreeting);雖然構造器和shadowRoot等特性值得關注,但最核心的是Web Components允許你使用瀏覽器標準定義封裝功能,這些代碼可以直接在瀏覽器控制臺運行。
使用Lit開發Web Components
現在讓我們用Lit實現相同功能:
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>`;
}
}這段代碼實現了與Web Components示例相同的功能,但代碼量和復雜度顯著降低。以@開頭的裝飾器(又稱注解)讓我們能簡潔地聲明customElement和name屬性。借助Lit的css函數(一個標簽模板字面量函數),我們不再需要默認構造器,也不再需要內聯CSS標記。
Lit還允許我們使用render方法返回由html函數生成的模板。html函數參數的內容可以結合HTML與變量插值,類似于JSX和其他模板語法,但注意我們使用${}而非{},并且使用this來引用組件。
最簡單的方式是通過Lit在線演練場嘗試。注意在演練場中需要切換TS(TypeScript)選項才能使注解生效(這個限制僅適用于演練場,構建時注解在JavaScript中同樣有效)。
為Lit組件添加響應式特性
現在讓我們實現更高級的響應式功能,使Lit的name變量可交互。我們將添加一個輸入框來實現雙向綁定:
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元素和事件處理函數。輸入框使用標準HTML文本類型,其value屬性前綴的.操作符是Lit實現雙向綁定的關鍵——它告訴Lit需要綁定的是JavaScript動態屬性而非靜態值。@input屬性將變更處理器指向我們的_handleNameInput函數,該函數使用標準DOM操作獲取輸入值并賦值給this.name變量,完成了雙向綁定的另一側。
使用Lit組件內部狀態
響應式庫的另一個核心特性是組件內部狀態管理。Lit同樣簡化了這個方面。例如要實現顯示/隱藏功能:
@state()
private _showSecretMessage = false;然后在模板中使用這個狀態變量:
${this._showSecretMessage
? html`<p>This is the secret message!</p>`
: '' /* 為false時不渲染內容 */
}這段模板代碼使用JavaScript三元運算符實現條件渲染。要切換狀態值,可以添加按鈕:
<button @click=${this._toggleSecretMessage}>
${this._showSecretMessage ? 'Hide' : 'Show'} Secret
</button>對應的點擊處理器非常簡單:
_toggleSecretMessage() {
this._showSecretMessage = !this._showSecretMessage;
}在Lit中渲染集合
讓我們看看Lit如何處理集合渲染。首先創建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函數遍歷輸出每個列表項。
使用Lit進行API調用
現在讓我們從Middle Earth轉向Westeros,從遠程API加載角色數據。首先創建內部state變量管理fetch promise:
@state()
private _characterDataPromise: Promise<unknown>;在構造器中初始化數據加載:
constructor() {
super();
this._characterDataPromise = this._fetchCharacterData();
}_fetchCharacterData函數實現如下:
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結果:
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>
`;總結
Lit蘊含了許多創新理念,其基于Web Components標準的特性使其受歡迎程度并不令人意外。關鍵問題在于Lit是否能成為React、Svelte和Vue等框架通用的組件系統。如果實現這一點,它將進入全新的發展階段。就目前而言,Lit本身就是一個可行的方案,特別適合重視標準合規性的項目。
本文所有示例的源代碼可以在我的GitHub倉庫中找到。
原文地址:https://www.infoworld.com/article/2334669/intro-to-lit-a-javascript-framework.html





























