Spec-Zone.ru › Lit 1

Справочник по синтаксису шаблонов

Шаблоны lit-html записываются с помощью шаблонных литералов JavaScript, помеченных тегом html. Содержимое литерала — это в основном обычный декларативный HTML:

html`<h1>Hello World</h1>`

Привязки или выражения обозначаются стандартным синтаксисом JavaScript для шаблонных литералов:

html`<h1>Hello ${name}</h1>`

Структура шаблона

Шаблоны lit-html должны быть корректно сформированным HTML, а привязки могут находиться только в определённых местах. Браузер разбирает шаблоны встроенным HTML-парсером до интерполяции каких-либо значений.

Предупреждений не будет. Большинство случаев некорректных шаблонов не обнаруживаются lit-html, поэтому вы не увидите предупреждений — только шаблоны, которые работают не так, как вы ожидаете. Поэтому уделяйте особое внимание правильной структуре шаблонов.

Чтобы шаблоны были корректно сформированы, следуйте этим правилам:

  • Шаблоны должны представлять собой корректно сформированный HTML, если заменить все выражения пустыми значениями.

  • Привязки могут находиться только в значениях атрибутов и текстовом содержимом.

    <!-- attribute value -->
    <div label="${label}"></div>
    
    <!-- text content -->
    <div>${textContent}</div>
  • Выражения не могут находиться там, где должны быть имена тегов или атрибутов.

    <!-- ERROR -->
    <${tagName}></${tagName}>
    
    <!-- ERROR -->
    <div ${attrName}=true></div>
  • Шаблоны могут содержать несколько элементов и текст на верхнем уровне.

  • Шаблоны не должны содержать незакрытых элементов — 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`<h1>Hello ${name}</h1>`

    Текстовые привязки могут находиться в любом месте текстового содержимого элемента.

  • Атрибутная:

    html`<div id=${id}></div>`
  • Булев атрибут:

    html`<input type="checkbox" ?checked=${checked}>`
  • Свойство:

    html`<input .value=${value}>`
  • Обработчик события:

    html`<button @click=${(e) => console.log('clicked')}>Click Me</button>`

Обработчики событий

Обработчиками событий могут быть функции или объекты с методом handleEvent. Обработчики передаются в качестве аргументов listener и options в addEventListener/removeEventListener, поэтому обработчик может содержать параметры обработчика событий, например capture, passive и once.

const listener = {
  handleEvent(e) {
    console.log('clicked');
  },
  capture: true,
};

html`<button @click=${listener}>Click Me</button>`

Поддерживаемые типы данных

Каждый тип привязки поддерживает разные типы значений:

  • Привязки текстового содержимого: множество типов, см. раздел Поддерживаемые типы данных для текстовых привязок.

  • Атрибутные привязки: все значения преобразуются в строки.

  • Привязки булевых атрибутов: все значения проверяются на истинность.

  • Привязки свойств: значения любого типа.

  • Привязки обработчиков событий: только функции или объекты — обработчики событий.

Поддерживаемые типы данных для текстовых привязок

Текстовые привязки поддерживают широкий диапазон типов значений:

  • Примитивные значения.
  • Объекты TemplateResult.
  • Узлы DOM.
  • Массивы или итерируемые объекты.

Примитивные значения: String, Number, Boolean, null, undefined

При интерполяции в текстовое содержимое или значения атрибутов примитивные значения преобразуются в строки. Они сравниваются с предыдущим значением, чтобы не обновлять DOM, если значение не изменилось.

TemplateResult

Шаблоны можно вкладывать друг в друга, передав TemplateResult в качестве значения выражения:

const header = html`<h1>Header</h1>`;

const page = html`
  ${header}
  <p>This is some text</p>
`;

Узел

В выражение в текстовой позиции можно передать любой узел DOM. Узел будет добавлен в дерево DOM в указанном месте и удалён из текущего родителя:

const div = document.createElement('div');
const page = html`
  ${div}
  <p>This is some text</p>
`;

Массивы / итерируемые объекты

Также поддерживаются массивы и итерируемые объекты, содержащие значения поддерживаемых типов. Они могут включать значения разных поддерживаемых типов.

const items = [1, 2, 3];
const list = () => html`items = ${items.map((i) => `item: ${i}`)}`;
const items = {
  a: 1,
  b: 23,
  c: 456,
};
const list = () => html`items = ${Object.entries(items)}`;

Управление потоком выполнения с помощью JavaScript

В lit-html нет встроенных конструкций управления потоком выполнения. Вместо этого используйте обычные выражения и инструкции JavaScript.

Условия с тернарными операторами

Тернарные выражения — отличный способ добавить условие прямо в строку:

html`
  ${user.isloggedIn
      ? html`Welcome ${user.name}`
      : html`Please log in`
  }
`;

Условия с инструкциями if

Условную логику можно выразить с помощью инструкций if вне шаблона, вычисляя значения для использования внутри шаблона:

getUserMessage() {
  if (user.isloggedIn) {
    return html`Welcome ${user.name}`;
  } else {
    return html`Please log in`;
  }
}

html`
  ${getUserMessage()}
`

Циклы с Array.map

Для отображения списков можно использовать Array.map, чтобы преобразовать список данных в список шаблонов:

html`
  <ul>
    ${items.map((i) => html`<li>${i}</li>`)}
  </ul>
`;

Циклические инструкции

const itemTemplates = [];
for (const i of items) {
  itemTemplates.push(html`<li>${i}</li>`);
}

html`
  <ul>
    ${itemTemplates}
  </ul>
`;

Встроенные директивы

Директивы — это функции, расширяющие возможности lit-html за счёт настройки способа отображения привязки.

В lit-html есть несколько встроенных директив.

  • asyncAppend и asyncReplace

  • cache

  • classMap

  • ifDefined

  • guard

  • live

  • repeat

  • styleMap

  • templateContent

  • unsafeHTML

  • unsafeSVG

  • until

asyncAppend и asyncReplace

asyncAppend(asyncIterable)
asyncReplace(asyncIterable)

Расположение: текстовые привязки

Асинхронные итераторы JavaScript предоставляют универсальный интерфейс для последовательного асинхронного доступа к данным. Как и в случае с обычным итератором, потребитель запрашивает следующий элемент данных вызовом next(), но у асинхронных итераторов next() возвращает Promise, что позволяет итератору предоставить элемент, когда он будет готов.

В lit-html есть две директивы для обработки асинхронных итераторов:

  • asyncAppend отображает значения асинхронно итерируемого объекта, добавляя каждое новое значение после предыдущего.

  • asyncReplace отображает значения асинхронно итерируемого объекта, заменяя предыдущее значение новым.

Пример:

import {asyncReplace} from 'lit-html/directives/async-replace.js';

const wait = (t) => new Promise((resolve) => setTimeout(resolve, t));
/**
 * Returns an async iterable that yields increasing integers.
 */
async function* countUp() {
  let i = 0;
  while (true) {
    yield i++;
    await wait(1000);
  }
}

render(html`
  Count: <span>${asyncReplace(countUp())}</span>.
`, document.body);

В ближайшем будущем ReadableStream станут асинхронно итерируемыми объектами, что позволит напрямую передавать потоковые fetch() в шаблон:

import {asyncAppend} from 'lit-html/directives/async-append.js';

// Endpoint that returns a billion digits of PI, streamed.
const url =
    'https://cors-anywhere.herokuapp.com/http://stuff.mit.edu/afs/sipb/contrib/pi/pi-billion.txt';

const streamingResponse = (async () => {
  const response = await fetch(url);
  return response.body.getReader();
})();
render(html`π is: ${asyncAppend(streamingResponse)}`, document.body);

cache

cache(conditionalTemplate)

Расположение: текстовые привязки

Кэширует отображённые узлы DOM шаблонов, когда они не используются. Аргумент conditionalTemplate — это выражение, которое может возвращать один из нескольких шаблонов. cache отображает текущее значение conditionalTemplate. При смене шаблона директива кэширует текущие узлы DOM, прежде чем переключиться на новое значение.

Пример:

import {cache} from 'lit-html/directives/cache.js';

const detailView = (data) => html`<div>...</div>`;
const summaryView = (data) => html`<div>...</div>`;

html`${cache(data.showDetails
  ? detailView(data)
  : summaryView(data)
)}`

При повторном отображении шаблона lit-html обновляет только изменённые части: он не создаёт и не удаляет больше узлов DOM, чем необходимо. Но при переключении с одного шаблона на другой lit-html необходимо удалить старые узлы DOM и отобразить новое дерево DOM.

Директива cache кэширует созданный DOM для заданной привязки и входного шаблона. В примере выше она кэширует DOM для обоих шаблонов: summaryView и detailView. При переключении с одного представления на другое lit-html достаточно подставить кэшированную версию нового представления и обновить её последними данными.

classMap

class=${classMap(classObj)}

Расположение: атрибутные привязки (должна быть единственной привязкой в атрибуте class)

Задаёт список классов на основе объекта. Каждый ключ объекта считается именем класса. Если соответствующее ключу значение истинно, этот класс добавляется элементу.

import {classMap} from 'lit-html/directives/class-map.js';

let classes = { highlight: true, enabled: true, hidden: false };

html`<div class=${classMap(classes)}>Classy text</div>`;
// renders as <div class="highlight enabled">Classy text</div>

classMap должна быть единственной привязкой в атрибуте class, но её можно сочетать со статическими значениями:

html`<div class="my-widget ${classMap(dynamicClasses)}">Static and dynamic</div>`;

ifDefined

ifDefined(value)

Расположение: атрибутные привязки

Для AttributeParts: задаёт атрибут, если значение определено, и удаляет его, если значение равно undefined.

Для других типов частей эта директива ничего не делает.

Пример:

import {ifDefined} from 'lit-html/directives/if-defined';

const myTemplate = () => html`
  <img src="/images/${ifDefined(image.filename)}">
`;

guard

guard(dependencies, valueFn)

Расположение: любое

Отображает значение, возвращаемое valueFn. Повторно вычисляет valueFn, только если идентичность одной из зависимостей изменилась.

Где:

  • dependencies — массив значений, изменения которых нужно отслеживать. (Для обратной совместимости dependencies может быть одним значением, не являющимся массивом.)
  • valueFn — функция, возвращающая значение, которое можно отобразить.

guard полезна при использовании неизменяемых структур данных: она позволяет отложить затратные операции до обновления данных.

Пример:

import {guard} from 'lit-html/directives/guard';

const template = html`
  <div>
    ${guard([immutableItems], () => immutableItems.map(item => html`${item}`))}
  </div>
`;

В этом случае массив immutableItems перебирается только при изменении ссылки на массив.

live

attr=${live(value)}

Расположение: атрибутные привязки или привязки свойств

При определении необходимости обновить значение сравнивает значение привязки с текущим значением в DOM, а не с ранее привязанным значением.

Это полезно в случаях, когда значение в DOM может измениться вне lit-html. Например, при привязке к свойству value элемента <input>, к тексту элемента с возможностью редактирования или к пользовательскому элементу, который изменяет собственные свойства или атрибуты.

В таких случаях, если значение в DOM изменилось, а значение, заданное с помощью привязок lit-html, — нет, lit-html не узнает, что значение в DOM нужно обновить, и оставит его без изменений. Если это не то, что вам нужно, и вы хотите в любом случае перезаписать значение в DOM привязанным значением, используйте директиву live().

Пример:

html`<input .value=${live(x)}>`

live() выполняет строгое сравнение с текущим значением в DOM и ничего не делает, если новое значение совпадает с ним. Это означает, что live() не следует использовать, если привязка вызывает преобразование типа. При использовании live() с атрибутной привязкой передавайте только строки, иначе привязка будет обновляться при каждом отображении.

repeat

repeat(items, keyfn, template)
repeat(items, template)

Расположение: текстовые привязки

Повторяет последовательность значений (обычно TemplateResults), сформированных из итерируемого объекта, и эффективно обновляет эти элементы при изменении итерируемого объекта. Если задан keyFn, при обновлениях сохраняется соответствие ключей узлам DOM, а при необходимости узлы перемещаются. Как правило, это наиболее эффективный способ использовать repeat, поскольку он сводит к минимуму лишнюю работу при добавлении и удалении элементов.

Пример:

import {repeat} from 'lit-html/directives/repeat';

const myTemplate = () => html`
  <ul>
    ${repeat(items, (i) => i.id, (i, index) => html`
      <li>${index}: ${i.name}</li>`)}
  </ul>
`;

Если keyFn не задан, repeat будет работать подобно обычному сопоставлению элементов со значениями, а узлы DOM могут повторно использоваться для других элементов.

О том, когда использовать repeat, а когда — стандартные средства управления потоком выполнения JavaScript, см. раздел Повторяющиеся шаблоны с директивой repeat.

styleMap

style=${styleMap(styles)}

Расположение: атрибутные привязки (должна быть единственной привязкой в атрибуте style)

Директива styleMap задаёт стили элемента на основе объекта. Каждый ключ объекта считается свойством стиля, а значение — значением этого свойства. Например:

import {styleMap} from 'lit-html/directives/style-map.js';

let styles = { backgroundColor: 'blue', color: 'white' };
html`<p style=${styleMap(styles)}>Hello style!</p>`;

Для свойств CSS с дефисами можно использовать эквивалентное имя в camelCase или заключить имя свойства в кавычки. Например, свойство CSS font-family можно записать как fontFamily или 'font-family':

{ fontFamily: 'roboto' }
{ 'font-family': 'roboto' }

styleMap должна быть единственной привязкой в атрибуте style, но её можно сочетать со статическими значениями:

html`<p style="color: white; ${styleMap(moreStyles)}">More styles!</p>`;

templateContent

templateContent(templateElement)

Расположение: текстовые привязки

Отображает содержимое элемента <template> как HTML.

Обратите внимание: содержимое шаблона должно контролироваться разработчиком, а не пользователем. Отображение пользовательских шаблонов с помощью этой директивы может привести к уязвимостям XSS.

Пример:

import {templateContent} from 'lit-html/directives/template-content';

const templateEl = document.querySelector('template#myContent');

const template = html`
  Here's some content from a template element:

  ${templateContent(templateEl)}`;

unsafeHTML

unsafeHTML(html)

Расположение: текстовые привязки

Отображает аргумент как HTML, а не как текст.

Обратите внимание: небезопасно использовать эту директиву с пользовательскими данными, которые не были очищены или экранированы, поскольку это может привести к уязвимостям межсайтового скриптинга.

Пример:

import {unsafeHTML} from 'lit-html/directives/unsafe-html.js';

const markup = '<div>Some HTML to render.</div>';
const template = html`
  Look out, potentially unsafe HTML ahead:
  ${unsafeHTML(markup)}
`;

unsafeSVG

unsafeSVG(svg)

Расположение: текстовые привязки

Отображает аргумент как SVG, а не как текст.

Обратите внимание: небезопасно использовать эту директиву с пользовательскими данными, которые не были очищены или экранированы, поскольку это может привести к уязвимостям межсайтового скриптинга.

Пример:

import {unsafeSVG} from 'lit-html/directives/unsafe-svg';

const svg = '<circle cx="50" cy="50" r="40" fill="red" />'

const template = html`
  Look out, potentially unsafe SVG ahead:
  <svg width="40" height="40" viewBox="0 0 100 100"
    xmlns="http://www.w3.org/2000/svg" version="1.1">
    ${unsafeSVG(svg)}
  </svg> `;

until

until(...values)

Расположение: любое

Отображает содержимое-заполнитель, пока не будет доступно окончательное содержимое.

Принимает последовательность значений, в том числе Promise. Значения отображаются в порядке приоритета: первый аргумент имеет наивысший приоритет, а последний — наименьший. Если значение является Promise, до его выполнения будет отображаться значение с более низким приоритетом.

Приоритет значений можно использовать для создания содержимого-заполнителя для асинхронных данных. Например, Promise с ожидающим содержимым можно передать первым аргументом с наивысшим приоритетом, а шаблон без Promise с индикатором загрузки — вторым аргументом с более низким приоритетом. Индикатор загрузки отображается сразу, а основное содержимое — после выполнения Promise.

Пример:

import {until} from 'lit-html/directives/until.js';

const content = fetch('./content.txt').then(r => r.text());

html`${until(content, html`<span>Loading...</span>`)}`

Редактировать эту страницу

© Google LLC
Licensed under the Creative Commons Attribution 3.0 Unported License.
https://lit.dev/docs/v1/lit-html/template-reference/

Spec-Zone.ru

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