Spec-Zone.ru › Lit 3

Выражения

Шаблоны 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}">`;

В этом примере для задания атрибута src должны быть определены оба свойства: this.imagePath и this.imageFile. Оператор нулевого слияния ?? возвращает значение справа, если значение слева — 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)}></button>`;

Дополнительные сведения см. в разделе 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 из модуля static-html Lit:

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();
    // html should be imported from `lit/static-html.js`
    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();
    // html should be imported from `lit/static-html.js`
    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/templates/expressions/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API