響應式控制器
響應式控制器是一個可以掛鉤到元件響應式更新週期的物件。控制器可以捆綁與功能相關的狀態和行為,使其可以在多個元件定義中重複使用。
您可以使用控制器來實作需要自己狀態和存取元件生命週期的功能,例如
- 處理滑鼠事件等全域事件
- 管理透過網路擷取資料等非同步任務
- 執行動畫
響應式控制器可讓您透過組合較小的片段來建立元件,而這些片段本身不是元件。它們可以被視為可重複使用、部分的元件定義,具有自己的身分和狀態。
響應式控制器在許多方面都類似於類別混入。主要區別在於它們具有自己的身分,並且不會新增到元件的原型中,這有助於包含其 API,並讓您每個宿主元件使用多個控制器實例。請參閱控制器與混入以瞭解更多詳細資訊。
使用控制器
連結至「使用控制器」的永久連結每個控制器都有自己的建立 API,但通常您會建立一個實例並將其儲存在元件中
class MyElement extends LitElement {
private clock = new ClockController(this, 1000);
}
與控制器實例相關聯的元件稱為宿主元件。
控制器實例會註冊自己以接收來自宿主元件的生命週期回呼,並在控制器有要渲染的新資料時觸發宿主更新。這就是 ClockController
範例如何定期渲染目前時間的方式。
控制器通常會公開一些功能,以在宿主的 render()
方法中使用。例如,許多控制器會有一些狀態,例如目前值
render() {
return html`
<div>Current time: ${this.clock.value}</div>
`;
}
由於每個控制器都有自己的 API,請參閱特定控制器文件以瞭解如何使用它們。
撰寫控制器
連結至「撰寫控制器」的永久連結響應式控制器是一個與宿主元件相關聯的物件,它會實作一個或多個宿主生命週期回呼或與其宿主互動。它可以透過多種方式實作,但我們將重點放在使用 JavaScript 類別,其中包含用於初始化的建構函式和用於生命週期的方法。
控制器初始化
連結至「控制器初始化」的永久連結控制器透過呼叫 host.addController(this)
向其宿主元件註冊自己。通常,控制器會儲存對其宿主元件的參考,以便稍後與其互動。
class ClockController implements ReactiveController {
private host: ReactiveControllerHost;
constructor(host: ReactiveControllerHost) {
// Store a reference to the host
this.host = host;
// Register for lifecycle updates
host.addController(this);
}
}
class ClockController {
constructor(host) {
// Store a reference to the host
this.host = host;
// Register for lifecycle updates
host.addController(this);
}
}
您可以新增其他建構函式參數以進行一次性設定。
class ClockController implements ReactiveController {
private host: ReactiveControllerHost;
timeout: number
constructor(host: ReactiveControllerHost, timeout: number) {
this.host = host;
this.timeout = timeout;
host.addController(this);
}
class ClockController {
constructor(host, timeout) {
this.host = host;
this.timeout = timeout;
host.addController(this);
}
將您的控制器向宿主元件註冊後,您可以將生命週期回呼和其他類別欄位和方法新增至控制器,以實作所需的狀態和行為。
生命週期
連結至「生命週期」的永久連結響應式控制器生命週期,在 ReactiveController
介面中定義,是響應式更新週期的子集。LitElement 會在其生命週期回呼期間呼叫任何已安裝的控制器。這些回呼是選用的。
hostConnected()
:- 在宿主連線時呼叫。
- 在建立
renderRoot
之後呼叫,因此此時將存在陰影根目錄。 - 適用於設定事件監聽器、觀察者等。
hostUpdate()
:- 在宿主的
update()
和render()
方法之前呼叫。 - 適用於在更新 DOM 之前讀取 DOM(例如,用於動畫)。
- 在宿主的
hostUpdated()
:- 在更新之後、在宿主的
updated()
方法之前呼叫。 - 適用於在修改 DOM 之後讀取 DOM(例如,用於動畫)。
- 在更新之後、在宿主的
hostDisconnected()
:- 在宿主斷線時呼叫。
- 適用於清理在
hostConnected()
中新增的項目,例如事件監聽器和觀察者。
如需更多資訊,請參閱響應式更新週期。
控制器宿主 API
連結至「控制器宿主 API」的永久連結響應式控制器宿主實作一個小的 API,用於新增控制器和請求更新,並負責呼叫其控制器的生命週期方法。
這是控制器宿主上公開的最小 API
addController(controller: ReactiveController)
removeController(controller: ReactiveController)
requestUpdate()
updateComplete: Promise<boolean>
您也可以建立特定於 HTMLElement
、ReactiveElement
、LitElement
的控制器,並且需要這些 API 的更多內容;甚至可以建立與特定元素類別或其他介面相關聯的控制器。
LitElement
和 ReactiveElement
是控制器宿主,但宿主也可以是其他物件,例如來自其他 Web 元件函式庫的基底類別、框架中的元件或其他控制器。
從其他控制器建立控制器
連結至「從其他控制器建立控制器」的永久連結控制器也可以由其他控制器組成。若要執行此操作,請建立子控制器並將宿主轉送給它。
class DualClockController implements ReactiveController {
private clock1: ClockController;
private clock2: ClockController;
constructor(host: ReactiveControllerHost, delay1: number, delay2: number) {
this.clock1 = new ClockController(host, delay1);
this.clock2 = new ClockController(host, delay2);
}
get time1() { return this.clock1.value; }
get time2() { return this.clock2.value; }
}
class DualClockController {
constructor(host, delay1, delay2) {
this.clock1 = new ClockController(host, delay1);
this.clock2 = new ClockController(host, delay2);
}
get time1() { return this.clock1.value; }
get time2() { return this.clock2.value; }
}
控制器與指令
連結至「控制器與指令」的永久連結將控制器與指令結合使用可能是一種非常強大的技術,特別是對於需要在渲染之前或之後執行工作的指令(如動畫指令);或需要參考樣板中特定元素的控制器。
有兩種主要模式可將控制器與指令搭配使用
- 控制器指令。這些指令本身是控制器,以便掛鉤到宿主生命週期。
- 擁有指令的控制器。這些控制器會建立一個或多個指令,以便在宿主的樣板中使用。
如需更多有關撰寫指令的資訊,請參閱自訂指令。
控制器指令
連結至「控制器指令」的永久連結響應式控制器不需要作為實例欄位儲存在宿主上。使用 addController()
新增到宿主的任何項目都是控制器。特別是,指令也可以是控制器。這使指令可以掛鉤到宿主生命週期。
擁有指令的控制器
連結至「擁有指令的控制器」的永久連結指令不需要是獨立函式,它們也可以是其他物件(例如控制器)的方法。這在控制器需要樣板中特定元素的參考時很有用。
例如,想像一下 ResizeController 讓您可以使用 ResizeObserver 觀察元素的大小。若要運作,我們需要一個 ResizeController 實例,以及一個放置在我們要觀察的元素上的指令
class MyElement extends LitElement {
private _textSize = new ResizeController(this);
render() {
return html`
<textarea ${this._textSize.observe()}></textarea>
<p>The width is ${this._textSize.contentRect?.width}</p>
`;
}
}
class MyElement extends LitElement {
_textSize = new ResizeController(this);
render() {
return html`
<textarea ${this._textSize.observe()}></textarea>
<p>The width is ${this._textSize.contentRect?.width}</p>
`;
}
}
若要實作此功能,請建立一個指令並從方法中呼叫它
class ResizeDirective {
/* ... */
}
const resizeDirective = directive(ResizeDirective);
export class ResizeController {
/* ... */
observe() {
// Pass a reference to the controller so the directive can
// notify the controller on size changes.
return resizeDirective(this);
}
}
待辦事項
- 檢閱並清理此範例
使用案例
連結至「使用案例」的永久連結響應式控制器非常通用,並且具有非常廣泛的可能使用案例。它們特別適合將元件連接到外部資源,例如使用者輸入、狀態管理或遠端 API。以下是一些常見的使用案例。
外部輸入
連結至「外部輸入」的永久連結響應式控制器可用於連接到外部輸入。例如,鍵盤和滑鼠事件、調整大小觀察器或變更觀察器。控制器可以提供輸入的目前值以在渲染中使用,並在值變更時要求宿主更新。
範例:MouseMoveController
連結至「範例:MouseMoveController」的永久連結此範例示範控制器如何在連線和斷線其宿主時執行設定和清除工作,以及在輸入變更時要求更新
非同步任務
連結至「非同步任務」的永久連結非同步任務,例如長時間執行的計算或網路 I/O,通常具有隨時間變更的狀態,並且需要在任務狀態變更時(完成、錯誤等)通知宿主。
控制器是捆綁任務執行和狀態的好方法,使其易於在元件內部使用。以控制器形式撰寫的任務通常具有宿主可以設定的輸入,以及宿主可以渲染的輸出。
@lit/task
包含一個通用的 Task
控制器,可以從宿主提取輸入、執行任務函式,並根據任務狀態渲染不同的樣板。
您可以使用 Task
來建立自訂控制器,其中包含為您的特定任務量身打造的 API。在這裡,我們將 Task
包裝在 NamesController
中,該控制器可以從示範 REST API 擷取指定的名稱清單之一。NameController
公開 kind
屬性作為輸入,以及一個 render()
方法,該方法可以根據任務狀態渲染四個樣板之一。任務邏輯以及它如何更新宿主都已從宿主元件中抽象出來。
待辦事項
- 動畫