Композиция компонентов
Самый распространённый способ справиться со сложностью и разделить код Lit на отдельные блоки — это композиция компонентов, то есть создание большого сложного компонента из небольших и простых компонентов. Представьте, что вам поручили реализовать экран пользовательского интерфейса:
Вы, вероятно, сможете определить области, реализация которых потребует определённых усилий. Скорее всего, именно они могут стать компонентами.
Изолировав сложные части в отдельных компонентах, вы значительно упростите задачу, а затем сможете объединить эти компоненты, чтобы создать целостный дизайн.
Например, в приведённом выше достаточно простом снимке экрана можно выделить несколько компонентов: верхнюю панель, кнопку меню, выдвижную панель с пунктами меню для навигации по текущему разделу и основную область содержимого. Каждый из них можно представить в виде компонента. Сложный компонент, например выдвижную панель с меню навигации, можно разделить на множество более мелких компонентов: саму панель, кнопку её открытия и закрытия, меню и отдельные пункты меню.
В Lit композиция выполняется добавлением элементов в шаблон — как встроенных HTML-элементов, так и пользовательских элементов.
render() {
return html`
<top-bar>
<icon-button icon="menu" slot="nav-button"></icon-button>
<span slot="title">Fuzzy</span>
</top-bar>
`;
}
Что делает компонент хорошим
Решая, как разделить функциональность, можно обратить внимание на несколько признаков, которые помогут определить, когда следует создать новый компонент. Часть интерфейса может стать хорошим кандидатом на роль компонента, если выполняется одно или несколько из следующих условий:
- У неё есть собственное состояние.
- У неё есть собственный шаблон.
- Она используется более чем в одном месте — в этом компоненте или в нескольких компонентах.
- Она сосредоточена на качественном выполнении одной задачи.
- У неё есть чётко определённый API.
Повторно используемые элементы управления, такие как кнопки, флажки и поля ввода, могут стать отличными компонентами. Но более сложные элементы интерфейса, такие как выдвижные панели и карусели, тоже хорошо подходят для компонентного подхода.
Передача данных вверх и вниз по дереву
При обмене данными с вложенными компонентами общее правило — следовать модели DOM: свойства вниз, события вверх.
Свойства вниз. Обычно предпочтительнее задавать свойства вложенного компонента, чем вызывать его методы. В шаблонах Lit и других декларативных системах шаблонов легко задавать свойства.
События вверх. На веб-платформе отправка событий — это стандартный способ передачи элементами информации вверх по дереву, часто в ответ на взаимодействие с пользователем. Это позволяет родительскому компоненту обработать событие или преобразовать его либо отправить повторно для компонентов-предков, расположенных выше по дереву.
Из этого правила следуют несколько выводов:
Компонент должен быть источником истины для вложенных компонентов в его теневом DOM. Вложенные компоненты не должны задавать свойства родительского компонента или вызывать его методы.
Если компонент изменяет собственное общедоступное свойство, он должен отправить событие, чтобы уведомить компоненты выше по дереву. Обычно такие изменения происходят в результате действий пользователя — например, нажатия кнопки или выбора пункта меню. Вспомните встроенный элемент
input, который отправляет событие, когда пользователь изменяет значение поля ввода.
Рассмотрим компонент меню, содержащий набор пунктов и предоставляющий свойства items и selectedItem в качестве части общедоступного API. Его структура DOM может выглядеть так:
Когда пользователь выбирает пункт, элемент my-menu должен обновить своё свойство selectedItem. Он также должен отправить событие, чтобы уведомить родительский компонент об изменении выбранного пункта. Весь процесс будет выглядеть примерно так:
- Пользователь взаимодействует с пунктом, в результате чего отправляется событие (стандартное событие, например
click, или событие, специфичное для компонентаmy-item). - Элемент
my-menuполучает событие и обновляет своё свойствоselectedItem. Он также может изменить состояние, чтобы выделить выбранный пункт. - Элемент
my-menuотправляет семантическое событие, указывающее на изменение выбранного пункта. Например, такое событие может называтьсяselected-item-changed. Поскольку это событие является частью API компонентаmy-menu, в данном контексте оно должно иметь понятное семантическое значение.
Дополнительные сведения об отправке событий и подписке на них см. в разделе События.
Передача данных между ветвями дерева
Свойства вниз и события вверх — хорошее правило, с которого можно начать. Но что делать, если необходимо обмениваться данными между двумя компонентами, которые не находятся в отношении родителя и потомка? Например, между двумя компонентами, являющимися соседними элементами в теневом дереве?
Одно из решений этой проблемы — использовать шаблон посредника. В этом шаблоне одноуровневые компоненты не взаимодействуют друг с другом напрямую. Вместо этого их взаимодействие посредничеством организует третья сторона.
Простой способ реализовать шаблон посредника — поручить родительскому компоненту обработку событий от дочерних компонентов, а затем при необходимости обновлять состояние дочерних компонентов, передавая изменённые данные вниз по дереву. Добавив посредника, можно передавать данные между ветвями дерева, используя привычный принцип «события вверх, свойства вниз».
В следующем примере элемент-посредник прослушивает события от элементов ввода и кнопки в своём теневом DOM. Он управляет состоянием кнопки, чтобы пользователь мог нажать Отправить только при наличии текста в поле ввода.
К другим шаблонам посредника относятся шаблоны в стиле Flux/Redux, в которых хранилище выступает посредником при изменении данных и обновляет компоненты с помощью подписок. Прямая подписка компонентов на изменения может избавить от необходимости передавать все нужные дочерним компонентам данные через каждый родительский компонент.
Дочерние элементы light DOM
Помимо узлов в теневом DOM, можно отображать дочерние узлы, предоставленные пользователем компонента. Например, стандартный элемент <select> может принимать набор элементов <option> в качестве дочерних и отображать их как пункты меню.
Чтобы отличать дочерние узлы от теневого DOM компонента, их иногда называют «light DOM». Например:
<top-bar> <icon-button icon="menu" slot="nav-button"></icon-button> <span slot="title">Fuzzy</span> </top-bar>
В этом примере у элемента top-bar есть два дочерних элемента light DOM, предоставленных пользователем: кнопка навигации и заголовок.
Работа с дочерними элементами light DOM отличается от работы с узлами в теневом DOM. Узлами в теневом DOM компонента управляет сам компонент, и к ним не следует обращаться извне. Дочерними элементами light DOM управляют извне, но компонент также может обращаться к ним. Пользователь компонента может в любой момент добавлять и удалять дочерние элементы light DOM, поэтому компонент не должен рассчитывать на неизменный набор дочерних узлов.
Компонент управляет тем, отображаются ли дочерние узлы и где именно, с помощью элемента <slot> в своём теневом DOM. Он также может получать уведомления о добавлении и удалении дочерних узлов, прослушивая событие slotchange.
Дополнительные сведения см. в разделах Отображение дочерних элементов с помощью слотов и Доступ к дочерним элементам, размещённым в слотах.
Фото суриката: Anggit Rizkianto, Unsplash.
© Google LLC
Licensed under the Creative Commons Attribution 3.0 Unported License.
https://lit.dev/docs/composition/component-composition/