表达式

Lit 模板可以包含称为表达式的动态值。表达式可以是任何 JavaScript 表达式。当模板被评估时,表达式会被计算,并且表达式的结果会在模板渲染时被包含进来。在 Lit 组件中,这意味着每当 render 方法被调用时,表达式都会被重新计算。

表达式只能放置在模板中的特定位置,而且表达式的解释方式取决于它出现的位置。元素标签内部的表达式会影响元素本身。元素内容中的表达式(即子节点所在的位置)会渲染成子节点或文本。

表达式的有效值根据表达式出现的位置而不同。一般来说,所有表达式都接受字符串和数字等原始值,而某些表达式支持额外的值类型。此外,所有表达式都可以接受_指令_,这些指令是定制表达式处理和渲染方式的特殊函数。有关更多信息,请参阅自定义指令

下面是一个快速参考,随后是关于每种表达式类型的更详细信息。

类型示例

子节点

属性

布尔属性

属性(Property)

事件监听器

元素指令

下面的基本示例展示了不同类型的表达式。

以下部分详细描述了每种表达式类型。有关模板结构的更多信息,请参阅格式良好的 HTML有效的表达式位置

在元素的开始标签和结束标签之间出现的表达式可以向元素添加子节点。例如:

或者:

子位置的表达式可以接受多种类型的值:

  • 原始值,如字符串、数字和布尔值。
  • 使用 html 函数创建的 TemplateResult 对象(如果表达式在 <svg> 元素内,则使用 svg 函数)。
  • DOM 节点。
  • 哨兵值 nothingnoChange
  • 任何支持类型的数组或可迭代对象。

Lit 可以渲染几乎所有原始值,并在插入到文本内容时将它们转换为字符串。

数字值如 5 将渲染为字符串 '5'。大整数(Bigint)也类似处理。

布尔值 true 将渲染为 'true',而 false 将渲染为 'false',但这种直接渲染布尔值的情况并不常见。通常,布尔值会在条件语句中用于渲染其他适当的值。有关条件的更多信息,请参阅条件渲染

空字符串 ''nullundefined 会被特殊处理,不会渲染任何内容。有关更多信息,请参阅移除子内容

Symbol 值无法转换为字符串,在子表达式中使用时会抛出错误。

Lit 提供了几个可以在子表达式中使用的特殊哨兵值。

noChange 哨兵值不会改变表达式的现有值。它通常用于自定义指令中。有关更多信息,请参阅表示无变化

nothing 哨兵值不渲染任何内容。有关更多信息,请参阅移除子内容

由于子位置的表达式可以返回 TemplateResult,你可以嵌套和组合模板:

这意味着你可以使用普通的 JavaScript 创建条件模板、重复模板等。

有关条件的更多信息,请参阅条件渲染

有关使用 JavaScript 创建重复模板的更多信息,请参阅列表

任何 DOM 节点都可以传递给子表达式。通常,DOM 节点应该通过使用 html 指定模板来渲染,但在需要时可以像这样直接渲染 DOM 节点。节点会在该点附加到 DOM 树上,因此会从其当前父节点中移除:

任何支持类型的数组或可迭代对象

Permalink to "任何支持类型的数组或可迭代对象"

表达式还可以返回任何支持类型的数组或可迭代对象,可以是任意组合。你可以将此功能与标准 JavaScript(如数组的 map 方法)一起使用来创建重复模板和列表。有关示例,请参阅列表

nullundefined、空字符串 '' 和 Lit 的 nothing 哨兵值会移除任何先前渲染的内容,且不渲染任何节点。

设置或移除子内容通常基于条件进行。有关更多信息,请参阅条件渲染为空

当表达式是带有 Shadow DOM 的元素的子元素,且该元素包含带有回退内容的 slot 时,不渲染节点非常重要。不渲染节点可确保渲染回退内容。有关更多信息,请参阅回退内容

除了使用表达式添加子节点外,你还可以使用它们来设置元素的特性(attribute)和属性(property)。

默认情况下,属性值中的表达式会设置该属性:

由于属性值始终是字符串,表达式应返回可转换为字符串的值。

如果表达式构成整个属性值,则可以省略引号。如果表达式仅构成属性值的一部分,则需要为整个值加引号:

注意,某些原始值在属性中会特殊处理。布尔值会转换为字符串,例如 false 渲染为 'false'undefinednull 作为属性会渲染为空字符串。

要设置布尔属性,请在属性名称前使用 ? 前缀。如果表达式计算结果为真值,则添加属性;如果为假值,则移除属性:

有时你可能只想在特定条件下设置属性,否则移除该属性。对于常见的"布尔属性",如 disabledhidden,如果你希望在为真值时将属性设置为空字符串,否则将其移除,请使用布尔属性。但有时,你可能需要不同的条件来添加或移除属性。

例如,考虑:

如果 this.imagePaththis.imageFile 未定义,则不应设置 src 属性,否则会发生无效的网络请求。

Lit 的 nothing 哨兵值通过在属性值中的任何表达式计算为 nothing 时移除属性来解决这个问题。

在此示例中,同时需要定义 this.imagePaththis.imageFile 属性才能设置 src 属性。??空值合并运算符在左侧值为 nullundefined 时返回右侧值。

Lit 还提供了 ifDefined 指令,它是 value ?? nothing 的语法糖。

你可能还希望在值不为真时移除属性,以便 false 或空字符串 '' 值移除属性。例如,考虑一个 this.ariaLabel 默认值为空字符串 '' 的元素:

在此示例中,仅当 this.ariaLabel 不是空字符串时才渲染 aria-label 属性。

设置或移除属性通常基于条件进行。有关更多信息,请参阅条件渲染为空

你可以使用 . 前缀和属性名称在元素上设置 JavaScript 属性:

上面代码的行为与直接设置 input 元素上的 value 属性相同,例如:

你可以使用属性表达式语法将复杂数据向下传递给子组件。例如,如果你有一个带有 listItems 属性的 my-list 组件,你可以传递一个对象数组给它:

请注意,此示例中的属性名称 listItems 是混合大小写的。虽然 HTML 属性不区分大小写,但 Lit 在处理模板时会保留属性名称的大小写。

有关组件属性的更多信息,请参阅响应式属性

模板还可以包含声明式事件监听器。使用前缀 @ 后跟事件名称。表达式应计算为事件监听器。

这类似于在按钮元素上调用 addEventListener('click', this.clickHandler)

事件监听器可以是普通函数,也可以是具有 handleEvent 方法的对象 — 与标准 addEventListener 方法的 listener 参数相同。

在 Lit 组件中,事件监听器会自动绑定到组件,因此你可以在处理程序中使用 this 值引用组件实例。

有关组件事件的更多信息,请参阅事件

你还可以添加一个表达式来访问元素实例,而不是元素上的单个属性或特性:

元素表达式仅适用于指令。元素表达式中的任何其他值类型都会被忽略。

可以在元素表达式中使用的一个内置指令是 ref 指令。它提供对渲染元素的引用。

有关更多信息,请参阅 ref

Lit 模板必须是格式良好的 HTML。在插值任何值之前,模板会由浏览器内置的 HTML 解析器解析。请遵循以下规则制作格式良好的模板:

  • 当所有表达式都替换为空值时,模板必须是格式良好的 HTML。

  • 模板可以有多个顶级元素和文本。

  • 模板_不应包含_未闭合的元素 — 它们将被 HTML 解析器关闭。

由于浏览器内置的解析器非常宽容,大多数格式不良的模板在运行时无法检测到,因此你不会看到警告 — 只会发现模板的行为不符合预期。我们建议使用代码检查工具IDE 插件在开发过程中发现模板中的问题。

表达式**只能出现**在 HTML 中可以放置属性值和子元素的位置。

元素表达式可以出现在开始标签中,位于标签名称之后:

表达式通常不应出现在以下位置:

  • 标签或属性名称的位置。Lit 不支持在此位置动态更改值,在开发模式下会产生错误。

  • <template> 元素内容内部(template 元素本身的属性表达式是允许的)。Lit 不会递归进入模板内容以动态更新表达式,在开发模式下会产生错误。

  • <textarea> 元素内容内部(textarea 元素本身的属性表达式是允许的)。请注意,Lit 可以向 textarea 渲染内容,但编辑 textarea 会破坏 Lit 用于动态更新的 DOM 引用,且 Lit 会在开发模式下发出警告。相反,应绑定到 textarea 的 .value 属性。

  • 类似地,在带有 contenteditable 属性的元素内部。相反,应绑定到元素的 .innerText 属性。

  • HTML 注释内部。Lit 不会更新注释中的表达式,表达式将以 Lit 标记字符串渲染。但这不会破坏后续表达式,因此在开发过程中注释掉可能包含表达式的 HTML 块是安全的。

  • 使用 ShadyCSS polyfill 时,在 <style> 元素内部。有关更多详细信息,请参阅表达式和样式元素

请注意,在使用静态表达式时,上述所有无效情况中的表达式都是有效的,尽管由于涉及效率低下(见下文),不应将其用于性能敏感的更新。

静态表达式返回特殊值,这些值在模板被 Lit 作为 HTML 处理之前就插入到模板中。由于它们成为模板静态 HTML 的一部分,它们可以放置在模板的任何位置 - 甚至是那些通常不允许表达式的位置,例如属性和标签名称。

要使用静态表达式,你必须从 Lit 的 static-html 模块导入特殊版本的 htmlsvg 模板标签:

static-html 模块包含支持静态表达式的 htmlsvg 标签函数,应该使用这些函数来代替 lit 模块中提供的标准版本。使用 literal 标签函数创建静态表达式。

你可以将静态表达式用于不太可能更改的配置选项,或用于自定义使用普通表达式无法自定义的模板部分 - 有关详细信息,请参阅有效的表达式位置部分。例如,my-button 组件可能渲染一个 <button> 标签,但子类可能渲染一个 <a> 标签。这是使用静态表达式的好地方,因为该设置不会频繁更改,而且无法使用普通表达式自定义 HTML 标签。

更改静态表达式的值代价很高。 使用 literal 值的表达式不应频繁更改,因为它们会导致重新解析新模板,并且每个变体都会保存在内存中。

在上面的示例中,如果模板重新渲染,且 this.captionthis.active 发生变化,Lit 会高效地更新模板,只更改受影响的表达式。但是,如果 this.tagthis.activeAttribute 发生变化,由于它们是用 literal 标记的静态值,会创建一个全新的模板;更新效率低下,因为会完全重新渲染 DOM。此外,更改传递给表达式的 literal 值会增加内存使用量,因为每个唯一模板都会缓存在内存中以提高重新渲染性能。

因此,最好将使用 literal 的表达式的更改保持在最低限度,并避免使用响应式属性来更改 literal 值,因为响应式属性本来就是为了可以更改而设计的。

静态值插值后,模板必须像普通 Lit 模板一样格式良好,否则模板中的动态表达式可能无法正常工作。有关更多信息,请参阅格式良好的 HTML部分。

在极少数情况下,你可能需要将未在脚本中定义的静态 HTML 插入到模板中,因此无法使用 literal 函数标记。对于这些情况,可以使用 unsafeStatic() 函数基于非脚本源的字符串创建静态 HTML。

仅用于可信内容。 注意 unsafeStatic() 中使用了 unsafe。传递给 unsafeStatic() 的字符串必须由开发者控制,不包含不可信内容,因为它将直接作为 HTML 解析,没有任何净化处理。不可信内容的例子包括查询字符串参数和用户输入的值。使用此指令渲染不可信内容可能导致 跨站脚本攻击 (XSS) 漏洞。

请注意,使用 unsafeStatic 的行为与 literal 具有相同的注意事项:由于更改值会导致解析新模板并将其缓存在内存中,因此它们不应频繁更改。