表達式
Lit 模板可以包含稱為表達式的動態值。表達式可以是任何 JavaScript 表達式。表達式會在模板評估時進行評估,而表達式的結果會在模板渲染時被包含。在 Lit 組件中,這表示每當呼叫 render
方法時就會發生。
表達式只能放置在模板中的特定位置,而且表達式的解讀方式取決於其出現的位置。元素標籤內的表達式會影響元素。元素內容內的表達式(子節點所在的位置)會渲染子節點或文字。
表達式的有效值會因表達式出現的位置而異。一般而言,所有表達式都接受字串和數字等原始值,而且某些表達式支援其他值類型。此外,所有表達式都可以接受指令,這些指令是自訂表達式處理和渲染方式的特殊函式。如需更多資訊,請參閱自訂指令。
以下是快速參考,以及有關每種表達式類型的更詳細資訊。
類型 | 範例 |
---|---|
| |
| |
| |
| |
| |
|
這個基本範例顯示了各種不同的表達式類型。
以下章節會更詳細地描述每一種表達式類型。如需有關模板結構的更多資訊,請參閱格式正確的 HTML 和有效的表達式位置。
子表達式
連結至「子表達式」的永久連結出現在元素的開始和結束標籤之間的表達式可以將子節點新增至元素。例如
html`<p>Hello, ${name}</p>`
或
html`<main>${bodyText}</main>`
子位置中的表達式可以採用多種值
- 像是字串、數字和布林值的原始值。
- 使用
html
函式 (或如果表達式位於<svg>
元素內,則使用svg
函式) 建立的TemplateResult
物件。 - DOM 節點。
- 哨兵值
nothing
和noChange
。 - 任何支援類型的陣列或可迭代物件。
Lit 可以渲染幾乎所有 原始值,並在內插到文字內容時將它們轉換為字串。
像 5
這樣的數值將會渲染字串 '5'
。Bigint 的處理方式也類似。
布林值 true
會渲染 'true'
,而 false
會渲染 'false'
,但是像這樣渲染布林值並不常見。布林值通常用於條件式中,以渲染其他適當的值。如需更多有關條件式的資訊,請參閱條件式。
空字串 ''
、null
和 undefined
會經過特殊處理,並且不渲染任何內容。如需更多資訊,請參閱移除子內容。
符號值無法轉換為字串,並且在放置於子表達式中時會擲回錯誤。
Lit 提供了一些特殊的哨兵值,可用於子表達式中。
noChange
哨兵值不會變更表達式的現有值。它通常用於自訂指令中。如需更多資訊,請參閱發出不變訊號。
nothing
哨兵值不渲染任何內容。如需更多資訊,請參閱移除子內容。
由於子位置中的表達式可以傳回 TemplateResult
,因此您可以巢狀和組合模板
const nav = html`<nav>...</nav>`;
const page = html`
${nav}
<main>...</main>
`;
這表示您可以使用純 JavaScript 來建立條件式模板、重複模板等等。
html`
${this.user.isloggedIn
? html`Welcome ${this.user.name}`
: html`Please log in`
}
`;
如需更多有關條件式的資訊,請參閱條件式。
如需更多有關使用 JavaScript 建立重複模板的資訊,請參閱列表。
DOM 節點
連結至「DOM 節點」的永久連結任何 DOM 節點都可以傳遞給子表達式。通常,DOM 節點應透過使用 html
指定模板來渲染,但如有需要,DOM 節點可以直接像這樣渲染。節點會在那時附加到 DOM 樹狀結構,並從任何目前的父節點中移除
const div = document.createElement('div');
const page = html`
${div}
<p>This is some text</p>
`;
任何支援類型的陣列或可迭代物件
連結至「任何支援類型的陣列或可迭代物件」的永久連結表達式也可以傳回任何支援類型的陣列或可迭代物件,且可以是任何組合。您可以將此功能與標準 JavaScript(例如陣列 map
方法)搭配使用,以建立重複模板和列表。如需範例,請參閱列表。
移除子內容
連結至「移除子內容」的永久連結值 null
、undefined
、空字串 ''
和 Lit 的 nothing 哨兵值會移除任何先前渲染的內容,且不渲染任何節點。
設定或移除子內容通常是根據條件來完成的。如需更多資訊,請參閱有條件地不渲染任何內容。
當表達式是包含具有回溯內容之 slot
的陰影 DOM 元素之子元素時,不渲染節點可能很重要。不渲染節點可確保渲染回溯內容。如需更多資訊,請參閱回溯內容。
屬性表達式
連結至「屬性表達式」的永久連結除了使用表達式新增子節點之外,您也可以使用表達式來設定元素的屬性和屬性。
預設情況下,屬性值中的表達式會設定屬性
html`<div class=${this.textClass}>Stylish text.</div>`;
由於屬性值一律為字串,因此表達式應傳回可轉換為字串的值。
如果表達式構成整個屬性值,您可以省略引號。如果表達式僅構成屬性值的一部分,則需要引用整個值
html`<img src="/images/${this.image}">`;
請注意,某些原始值在屬性中會經過特殊處理。布林值會轉換為字串,因此,例如,false
會渲染 'false'
。undefined
和 null
都會以空字串渲染為屬性。
布林屬性
連結至「布林屬性」的永久連結若要設定布林屬性,請將 ?
字首與屬性名稱搭配使用。如果表達式評估為真值,則會新增屬性,如果評估為假值,則會移除屬性
html`<div ?hidden=${!this.showAdditional}>This text may be hidden.</div>`;
移除屬性
連結至「移除屬性」的永久連結有時您只想在特定條件下設定屬性,否則會移除該屬性。對於常見的「布林屬性」(例如 disabled
和 hidden
),您想要針對真值將屬性設定為空字串,否則將其移除,請使用布林屬性。不過,有時您可能需要不同的條件來新增或移除屬性。
例如,請考慮
html`<img src="/images/${this.imagePath}/${this.imageFile}">`;
如果 this.imagePath
或 this.imageFile
未定義,則不應設定 src
屬性,否則會發生無效的網路要求。
Lit 的 nothing 哨兵值會在屬性值中的任何表達式評估為 nothing
時,藉由移除屬性來解決此問題。
html`<img src="/images/${this.imagePath ?? nothing}/${this.imageFile ?? nothing}">`;
在這個範例中,必須定義 this.imagePath
和 this.imageFile
屬性兩者,才能設定 src
屬性。如果左邊的值為 null
或 undefined
,空值合併運算符 ??
會返回右邊的值。
Lit 還提供了一個 ifDefined 指令,它是 value ?? nothing
的語法糖。
html`<img src="/images/${ifDefined(this.imagePath)}/${ifDefined(this.imageFile)}">`;
您可能也希望在值不是 truthy 時移除屬性,以便 false
或空字串 ''
的值會移除屬性。例如,考慮一個 this.ariaLabel
預設值為空字串 ''
的元素。
html`<button aria-label="${this.ariaLabel || nothing}"></button>`
在這個範例中,只有在 this.ariaLabel
不是空字串時才會渲染 aria-label
屬性。
設定或移除屬性通常是基於條件完成的。請參閱 有條件地渲染空值 以獲取更多資訊。
屬性表達式
“屬性表達式”的永久連結您可以使用 .
前綴和屬性名稱在元素上設定 JavaScript 屬性
html`<input .value=${this.itemCount}>`;
上述程式碼的行為與直接在 input
元素上設定 value
屬性相同,例如:
inputEl.value = this.itemCount;
您可以使用屬性表達式語法將複雜的數據向下傳遞到子元件。例如,如果您有一個具有 listItems
屬性的 my-list
元件,您可以將物件陣列傳遞給它
html`<my-list .listItems=${this.items}></my-list>`;
請注意,此範例中的屬性名稱—listItems
—是混合大小寫。雖然 HTML *屬性* 不區分大小寫,但 Lit 在處理範本時會保留屬性名稱的大小寫。
有關元件屬性的更多資訊,請參閱 響應式屬性。
事件監聽器表達式
“事件監聽器表達式”的永久連結範本也可以包含宣告式事件監聽器。使用前綴 @
後面跟著事件名稱。表達式應求值為事件監聽器。
html`<button @click=${this.clickHandler}>Click Me!</button>`;
這類似於在按鈕元素上呼叫 addEventListener('click', this.clickHandler)
。
事件監聽器可以是普通函數,也可以是具有 handleEvent
方法的物件—與標準 addEventListener
方法的 listener
引數相同。
在 Lit 元件中,事件監聽器會自動繫結到元件,因此您可以在處理常式內使用 this
值來引用元件實例。
clickHandler() {
this.clickCount++;
}
有關元件事件的更多資訊,請參閱 事件。
元素表達式
“元素表達式”的永久連結您也可以新增一個存取元素實例的表達式,而不是元素上的單個屬性或特性
html`<div ${myDirective()}></div>`
元素表達式僅適用於 指令。元素表達式中的任何其他值類型都會被忽略。
可以在元素表達式中使用的內建指令之一是 ref
指令。它提供了對渲染元素的引用。
html`<button ${ref(this.myRef)}></button>`;
有關更多資訊,請參閱 ref。
格式正確的 HTML
“格式正確的 HTML”的永久連結Lit 範本必須是格式正確的 HTML。範本會在插入任何值之前由瀏覽器的內建 HTML 解析器解析。請遵循以下規則以獲得格式正確的範本
當所有表達式都替換為空值時,範本必須是格式正確的 HTML。
範本可以有多個頂層元素和文字。
範本 *不應包含* 未關閉的元素—它們會由 HTML 解析器關閉。
// HTML parser closes this div after "Some text"
const template1 = html`<div class="broken-div">Some text`;
// When joined, "more text" does not end up in .broken-div
const template2 = html`${template1} more text. </div>`;
有效的表達式位置
“有效的表達式位置”的永久連結表達式**只能發生**在您可以在 HTML 中放置屬性值和子元素的位置。
<!-- attribute values -->
<div label=${label}></div>
<button ?disabled=${isDisabled}>Click me!</button>
<input .value=${currentValue}>
<button @click=${this.handleClick()}>
<!-- child content -->
<div>${textContent}</div>
元素表達式可以出現在標籤名稱之後的開始標籤內
<div ${ref(elementReference)}></div>
無效的位置
“無效的位置”的永久連結表達式通常不應出現在以下位置
標籤或屬性名稱將出現的位置。Lit 不支援在此位置動態變更值,並且會在開發模式下產生錯誤。
<!-- ERROR -->
<${tagName}></${tagName}>
<!-- ERROR -->
<div ${attrName}=true></div>
在
<template>
元素內容內(允許在範本元素本身上使用屬性表達式)。Lit 不會遞迴到範本內容中以動態更新表達式,並且會在開發模式下產生錯誤。<!-- ERROR -->
<template>${content}</template>
<!-- OK -->
<template id="${attrValue}">static content ok</template>
在
<textarea>
元素內容內(允許在 textarea 元素本身上使用屬性表達式)。請注意,Lit 可以將內容渲染到 textarea 中,但是編輯 textarea 會破壞 Lit 用於動態更新的 DOM 引用,並且 Lit 會在開發模式下發出警告。相反,請繫結到 textarea 的.value
屬性。<!-- BEWARE -->
<textarea>${content}</textarea>
<!-- OK -->
<textarea .value=${content}></textarea>
<!-- OK -->
<textarea id="${attrValue}">static content ok</textarea>
同樣地,在具有
contenteditable
屬性的元素內。相反,請繫結到元素的.innerText
屬性。<!-- BEWARE -->
<div contenteditable>${content}</div>
<!-- OK -->
<div contenteditable .innerText=${content}></div>
<!-- OK -->
<div contenteditable id="${attrValue}">static content ok</div>
在 HTML 註解內。Lit 不會更新註解中的表達式,而是會以 Lit 令牌字串渲染表達式。但是,這不會破壞後續的表達式,因此在開發期間註解掉可能包含表達式的 HTML 區塊是安全的。
<!-- will not update: ${value} -->
在使用 ShadyCSS 填充時,在
<style>
元素內。有關更多詳細資訊,請參閱 表達式和樣式元素。
請注意,當使用 靜態表達式時,以上所有無效情況中的表達式都是有效的,儘管由於涉及的效率低下,這些不應用於效能敏感的更新(請參閱下文)。
靜態表達式
“靜態表達式”的永久連結靜態表達式返回特殊值,這些值會在 Lit 將範本作為 HTML 處理 *之前* 插入到範本中。由於它們成為範本靜態 HTML 的一部分,因此可以放置在範本中的任何位置 - 甚至是通常不允許使用表達式的位置,例如在屬性和標籤名稱中。
要使用靜態表達式,您必須從 Lit 的 static-html
模組匯入特殊版本的 html
或 svg
範本標籤
import {html, literal} from 'lit/static-html.js';
static-html
模組包含支援靜態表達式的 html
和 svg
標籤函數,應使用它們來代替 lit
模組中提供的標準版本。使用 literal
標籤函數建立靜態表達式。
您可以將靜態表達式用於不太可能變更的組態選項,或用於自訂您無法使用一般表達式自訂的範本部分 - 請參閱有關 有效的表達式位置 的章節以獲取詳細資訊。例如,my-button
元件可能會渲染 <button>
標籤,但子類別可能會改為渲染 <a>
標籤。這是使用靜態表達式的好地方,因為設定不會經常變更,並且無法使用一般表達式自訂 HTML 標籤。
import {LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import {html, literal} from 'lit/static-html.js';
@customElement('my-button')
class MyButton extends LitElement {
tag = literal`button`;
activeAttribute = literal`active`;
@property() caption = 'Hello static';
@property({type: Boolean}) active = false;
render() {
return html`
<${this.tag} ${this.activeAttribute}=${this.active}>
<p>${this.caption}</p>
</${this.tag}>`;
}
}
import {LitElement} from 'lit';
import {html, literal} from 'lit/static-html.js';
class MyButton extends LitElement {
static properties = {
caption: {},
active: {type: Boolean},
};
tag = literal`button`;
activeAttribute = literal`active`;
constructor() {
super();
this.caption = 'Hello static';
this.active = false;
}
render() {
return html`
<${this.tag} ${this.activeAttribute}=${this.active}>
<p>${this.caption}</p>
</${this.tag}>`;
}
}
customElements.define('my-button', MyButton);
@customElement('my-anchor')
class MyAnchor extends MyButton {
tag = literal`a`;
}
class MyAnchor extends MyButton {
tag = literal`a`;
}
customElements.define('my-anchor', MyAnchor);
變更靜態表達式的值成本很高。使用 literal
值的表達式不應經常變更,因為它們會導致重新解析新的範本,並且每個變體都保存在記憶體中。
在上面的範例中,如果範本重新渲染並且 this.caption
或 this.active
變更,Lit 會有效地更新範本,僅變更受影響的表達式。但是,如果 this.tag
或 this.activeAttribute
變更,由於它們是使用 literal
標記的靜態值,因此會建立一個全新的範本;更新效率不高,因為 DOM 會完全重新渲染。此外,變更傳遞給表達式的 literal
值會增加記憶體使用量,因為每個唯一的範本都會快取在記憶體中以提高重新渲染效能。
基於這些原因,最好將使用 literal
的表達式的變更保持在最低限度,並避免使用響應式屬性來變更 literal
值,因為響應式屬性旨在變更。
模板結構
“範本結構”的永久連結在插入靜態值後,範本必須像一般 Lit 範本一樣格式正確,否則範本中的動態表達式可能無法正常運作。請參閱 格式正確的 HTML 章節以獲取更多資訊。
非文字靜態
“非文字靜態”的永久連結在極少數情況下,您可能需要將靜態 HTML 插入到未在腳本中定義的範本中,因此無法使用 literal
函數進行標記。對於這些情況,可以使用 unsafeStatic()
函數基於來自非腳本來源的字串建立靜態 HTML。
import {html, unsafeStatic} from 'lit/static-html.js';
僅適用於受信任的內容。請注意 unsafeStatic()
中使用 *unsafe*。傳遞給 unsafeStatic()
的字串必須由開發人員控制,且不包含不受信任的內容,因為它將直接解析為 HTML,而無需進行清理。不受信任的內容範例包括查詢字串參數和使用者輸入中的值。使用此指令渲染的不受信任內容可能會導致 跨站腳本 (XSS) 漏洞。
@customElement('my-button')
class MyButton extends LitElement {
@property() caption = 'Hello static';
@property({type: Boolean}) active = false;
render() {
// These strings MUST be trusted, otherwise this is an XSS vulnerability
const tag = getTagName();
const activeAttribute = getActiveAttribute();
return html`
<${unsafeStatic(tag)} ${unsafeStatic(activeAttribute)}=${this.active}>
<p>${this.caption}</p>
</${unsafeStatic(tag)}>`;
}
}
class MyButton extends LitElement {
static properties = {
caption: {},
active: {type: Boolean},
};
constructor() {
super();
this.caption = 'Hello static';
this.active = false;
}
render() {
// These strings MUST be trusted, otherwise this is an XSS vulnerability
const tag = getTagName();
const activeAttribute = getActiveAttribute();
return html`
<${unsafeStatic(tag)} ${unsafeStatic(activeAttribute)}=${this.active}>
<p>${this.caption}</p>
</${unsafeStatic(tag)}>`;
}
}
customElements.define('my-button', MyButton);
請注意,使用 unsafeStatic
的行為與 literal
具有相同的注意事項:因為變更值會導致解析新的範本並快取在記憶體中,所以它們不應經常變更。