表达式
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(例如 Array map
方法)创建重复模板和列表。有关示例,请参见列表。
移除子内容
指向“移除子内容”的永久链接值 null
、undefined
、空字符串 ''
以及 Lit 的 nothing 哨兵值会移除任何先前渲染的内容,并且不渲染任何节点。
设置或移除子内容通常基于条件。有关更多信息,请参见有条件地渲染为空。
当表达式是包含 slot
(带有回退内容)的 Shadow 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)}">`;
您可能还想在值不为真值时移除该属性,以便值 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 polyfill 时,在
<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
相同:因为更改值会导致解析新的模板并缓存在内存中,因此它们不应频繁更改。