Выражения
Шаблоны Lit могут содержать динамические значения, называемые выражениями. Выражением может быть любое выражение JavaScript. Выражение вычисляется при вычислении шаблона, а его результат включается при отрисовке шаблона. В компоненте Lit это происходит каждый раз, когда вызывается метод render.
Выражения можно размещать только в определённых местах шаблона, а способ интерпретации выражения зависит от места, в котором оно находится. Выражения внутри самого тега элемента влияют на элемент. Выражения внутри содержимого элемента, где располагаются дочерние узлы, отрисовывают дочерние узлы или текст.
Допустимые значения выражений различаются в зависимости от их расположения. Как правило, все выражения принимают примитивные значения, например строки и числа, а некоторые выражения поддерживают дополнительные типы значений. Кроме того, все выражения могут принимать директивы — специальные функции, позволяющие настроить обработку и отрисовку выражения. Подробнее см. в разделе Пользовательские директивы.
Ниже приведена краткая справка, за которой следует более подробная информация о каждом типе выражений.
| Тип | Пример |
|---|---|
html`
<h1>Hello ${name}</h1>
<ul>
${listItems}
</ul>` |
|
html`<div class=${highlightClass}></div>` |
|
html`<div ?hidden=${!show}></div>` |
|
html`<input .value=${value}>` |
|
html`<button @click=${this._clickHandler}>Go</button>` |
|
html`<input ${ref(inputRef)}>` |
В этом простом примере показаны различные типы выражений.
В следующих разделах подробнее описан каждый тип выражений. Дополнительную информацию о структуре шаблонов см. в разделах Корректный HTML и Допустимые места для выражений.
Дочерние выражения
Выражение, расположенное между открывающим и закрывающим тегами элемента, может добавлять к элементу дочерние узлы. Например:
html`<p>Hello, ${name}</p>`
Или:
html`<main>${bodyText}</main>`
В дочерней позиции выражения могут принимать значения многих типов:
- Примитивные значения, например строки, числа и логические значения.
-
Объекты
TemplateResult, созданные с помощью функцииhtml(или функцииsvg, если выражение находится внутри элемента<svg>). - Узлы DOM.
- Служебные значения
nothingиnoChange. - Массивы или итерируемые объекты с любыми поддерживаемыми типами значений.
Примитивные значения
Lit может отрисовывать почти все примитивные значения и преобразует их в строки при вставке в текстовое содержимое.
Числовые значения, например 5, отрисовываются как строка '5'. Аналогичным образом обрабатываются значения типа BigInt.
Логическое значение true отрисовывается как 'true', а false — как 'false', однако отрисовка логических значений таким образом встречается редко. Обычно логические значения используются в условных выражениях для отрисовки других подходящих значений. Подробнее об условных выражениях см. в разделе Условные выражения.
Пустая строка '', null и undefined обрабатываются особым образом и ничего не отрисовывают. Подробнее см. в разделе Удаление дочернего содержимого.
Значения Symbol нельзя преобразовать в строки, поэтому при размещении в дочерних выражениях они вызывают ошибку.
Служебные значения
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 следует отрисовывать, указывая шаблон с помощью html, однако при необходимости узел DOM можно отрисовать напрямую, как показано ниже. В этом случае узел добавляется в дерево DOM и удаляется из своего текущего родителя:
const div = document.createElement('div');
const page = html`
${div}
<p>This is some text</p>
`;
Массивы или итерируемые объекты с любыми поддерживаемыми типами значений
Выражение также может возвращать массив или итерируемый объект, содержащий любую комбинацию поддерживаемых типов значений. Эту возможность можно сочетать со стандартными средствами JavaScript, например методом map массива, для создания повторяющихся шаблонов и списков. Примеры см. в разделе Списки.
Удаление дочернего содержимого
Значения null, undefined, пустая строка '' и служебное значение Lit nothing удаляют ранее отрисованное содержимое и не отрисовывают узлов.
Добавление или удаление дочернего содержимого часто выполняется в зависимости от условия. Подробнее см. в разделе Условная отрисовка пустого содержимого.
Отсутствие отрисованного узла может быть важно, если выражение является дочерним для элемента с Shadow DOM, содержащего slot с резервным содержимым. Отсутствие узла гарантирует, что будет отрисовано резервное содержимое. Подробнее см. в разделе Резервное содержимое.
Выражения атрибутов
Помимо добавления дочерних узлов с помощью выражений, с их помощью можно задавать атрибуты и свойства элементов.
По умолчанию выражение в значении атрибута задаёт этот атрибут:
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 ?? возвращает значение справа, если значение слева равно 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>`
В этом примере атрибут aria-label отрисовывается, только если this.ariaLabel не является пустой строкой.
Добавление или удаление атрибута часто выполняется в зависимости от условия. Подробнее см. в разделе Условная отрисовка пустого содержимого.
Выражения свойств
Задать свойство JavaScript для элемента можно с помощью префикса . и имени свойства:
html`<input .value=${this.itemCount}>`;
Этот код работает так же, как и прямое задание свойства value элемента input, например:
inputEl.value = this.itemCount;
Синтаксис выражений свойств можно использовать для передачи сложных данных по дереву вложенным компонентам. Например, если у вас есть компонент my-list со свойством listItems, ему можно передать массив объектов:
html`<my-list .listItems=${this.items}></my-list>`;
Обратите внимание, что имя свойства в этом примере — listItems — содержит символы в верхнем и нижнем регистре. Хотя HTML-атрибуты не чувствительны к регистру, Lit сохраняет регистр имён свойств при обработке шаблона.
Дополнительную информацию о свойствах компонентов см. в разделе Реактивные свойства.
Выражения обработчиков событий
Шаблоны также могут содержать декларативные обработчики событий. Используйте префикс @, за которым следует имя события. Выражение должно возвращать обработчик события.
html`<button @click=${this.clickHandler}>Click Me!</button>`;
Это аналогично вызову addEventListener('click', this.clickHandler) для элемента button.
Обработчиком события может быть обычная функция или объект с методом handleEvent — так же, как аргумент listener стандартного метода addEventListener.
В компоненте Lit обработчик события автоматически привязывается к компоненту, поэтому внутри обработчика можно использовать значение this для ссылки на экземпляр компонента.
clickHandler() {
this.clickCount++;
}
Дополнительную информацию о событиях компонентов см. в разделе События.
Выражения элементов
Также можно добавить выражение, обращающееся к экземпляру элемента, а не к отдельному свойству или атрибуту элемента:
html`<div ${myDirective()}></div>`
Выражения элементов работают только с директивами. Любые другие типы значений в выражении элемента игнорируются.
Одна из встроенных директив, которую можно использовать в выражении элемента, — директива ref. Она предоставляет ссылку на отрисованный элемент.
html`<button ${ref(this.myRef)}`;
Подробнее см. в разделе ref.
Корректный 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>`;
Встроенный парсер браузера очень терпим к ошибкам, поэтому большинство некорректных шаблонов невозможно обнаружить во время выполнения: предупреждений не будет, но шаблоны могут работать не так, как вы ожидаете. Рекомендуем использовать инструменты линтинга и плагины для IDE, чтобы находить проблемы в шаблонах во время разработки.
Допустимые места для выражений
Выражения можно размещать только в тех местах 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>(выражения атрибутов самого элемента template допустимы). Lit не обходит содержимое template для динамического обновления выражений и выдаст ошибку в режиме разработки.<!-- ERROR --> <template>${content}</template> <!-- OK --> <template id="${attrValue}">static content ok</template> -
Внутри содержимого элемента
<textarea>(выражения атрибутов самого элемента textarea допустимы). Обратите внимание: Lit может отрисовывать содержимое в textarea, однако редактирование textarea нарушит ссылки на DOM, которые Lit использует для динамического обновления, и Lit выдаст предупреждение в режиме разработки. Вместо этого привязывайтесь к свойству.valueэлемента textarea.<!-- 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} --> Внутри элементов
<style>при использовании полифила ShadyCSS. Подробнее см. в разделе Выражения и элементы style.
Обратите внимание: во всех перечисленных выше недопустимых случаях выражения допустимы при использовании статических выражений, однако из-за неэффективности такого подхода их не следует использовать при обновлениях, чувствительных к производительности (см. ниже).
Статические выражения
Статические выражения возвращают специальные значения, которые вставляются в шаблон до того, как Lit обработает его как HTML. Поскольку они становятся частью статического HTML шаблона, их можно размещать в любом месте — даже там, где обычные выражения, как правило, запрещены, например в именах атрибутов и тегов.
Чтобы использовать статические выражения, необходимо импортировать специальную версию шаблонного тега html или svg из модуля Lit static-html:
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';
Только для доверенного содержимого. Обратите внимание на слово unsafe в unsafeStatic(). Строка, передаваемая в 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. Поскольку изменение значений приводит к повторному разбору шаблона и его кэшированию в памяти, эти значения не должны часто меняться.
© Google LLC
Licensed under the Creative Commons Attribution 3.0 Unported License.
https://lit.dev/docs/v2/templates/expressions/