Построение UI с помощью компонентов¶
Что такое компоненты?¶
Компонент - это функция, определяющая часть пользовательского интерфейса. Они являются строительными блоками приложения Solid.
Давайте создадим наш первый компонент. Этот компонент будет отображать Hello World!
внутри элемента <div>
. Для этого в папке src
необходимо создать файл HelloWorld.tsx
. Затем скопируйте в него следующее содержимое:
1 2 3 |
|
В нашем первом компоненте есть несколько моментов, на которые следует обратить внимание:
- Имя нашего компонента,
HelloWorld
, имеет заглавную букву (иногда ее называют паскалевской), причем Hello и World написаны с большой буквы. Это помогает нам отличать компоненты от других функций JavaScript. - Наш компонент возвращает нечто, очень похожее на HTML, но это не так — это JSX.
React
Хотя компоненты Solid похожи на компоненты React, они отличаются друг от друга по нескольким признакам. Функции компонентов Solid запускаются только один раз для настройки и больше не запускаются. Если вы привыкли помещать вычисления в тело функции компонента React и ожидать, что они будут повторно вычисляться при рендеринге компонента, то в Solid вам, возможно, придется использовать другой подход. Не волнуйтесь — мы рассмотрим все лучшие практики в этом руководстве.
Как и в React, в файл можно включать сколько угодно компонентов. Можно даже написать компонент внутри другого!
Vue
В Vue компоненты имеют жизненный цикл, который важен для работы с ними. В Solid компоненты - это просто функции, которые группируют ваш код. У них нет жизненных циклов, а система рендеринга Solid основана на реактивных функциях, которые вы используете— мы еще вернемся к этому!
В Solid можно включать в один файл сколько угодно компонентов. Можно даже написать компонент внутри другого!
Svelte
В Svelte компоненты имеют жизненные циклы, которые важны для работы с ними. В Solid компоненты - это просто функции, которые группируют ваш код. У них нет жизненных циклов, а система рендеринга Solid основана на реактивных функциях, которые вы используете — мы еще вернемся к этому!
В Svelte используются однофайловые компоненты, а в Solid - нет. В один файл можно включать сколько угодно компонентов. Можно даже написать компонент внутри другого!
Angular
В Angular компоненты имеют жизненный цикл, который важен для работы с ними. В Solid компоненты - это просто функции, которые группируют ваш код. У них нет жизненных циклов, а система рендеринга Solid основана не на границах компонентов, а на реактивных функциях, которые вы используете — мы еще вернемся к этому! Еще один момент: если в одном файле можно написать несколько компонентов Angular, используя свойство-декоратор компонента template
, то в Solid это реализовано на другом уровне.
В Solid можно не только включать сколько угодно компонентов в один файл, но даже писать компонент вместе с другим!
Использование компонента¶
Давайте выведем этот компонент на экран. Экспортируем его из файла HelloWorld.tsx
, заменив function
на export function
.
Затем заменим шаблон в файле App.tsx
на код, который импортирует и отображает наш компонент:
1 2 3 |
|
1 2 3 4 5 6 7 8 9 10 |
|
Мы использовали наш компонент так же, как и любой другой элемент: импортировав функцию HelloWorld
, мы теперь можем использовать <HelloWorld />
.
Построение с использованием компонентов позволяет разделить код и использовать его повторно. Компоненты позволяют нам создавать строительные блоки, которые мы можем соединять вместе для создания новых частей.
Что такое JSX?¶
Как мы узнали выше, компоненты Solid возвращают JSX. JSX - это HTML-подобный синтаксис, используемый в JavaScript-коде Solid для представления части объектной модели документа (DOM). В нашем примере HelloWorld.tsx
, приведенном выше, мы вернули следующий JSX из нашего компонента:
1 |
|
Этот JSX представляет собой элемент <div>
с текстом Hello World!
внутри. Это так же, как если бы мы написали его в HTML-документе. Чтобы продемонстрировать возможности и гибкость Solid, давайте добавим в наш элемент <div>
несколько встроенных стилей:
1 2 3 |
|
В результате будет создан div
с синим цветом фона и белым текстом:
Попытка создать стилизованный div
с помощью JavaScript без JSX будет более сложной:
1 2 3 |
|
vue
Vue имеет свой собственный "язык шаблонов" в специальном типе файлов под названием .vue
. .jsx
выполняет аналогичную роль в Solid, но не интегрирован непосредственно в JavaScript. Мы можем написать JSX-элемент в любом месте, где мы могли бы написать выражение JavaScript, и нет никакого принудительного разделения между шаблоном и данными.
svelte
В Svelte имеется собственный "язык шаблонов" в специальном типе файлов .svelte
..jsx
выполняет аналогичную роль в Solid, но не интегрирован непосредственно в JavaScript. Мы можем написать JSX-элемент в любом месте, где мы могли бы написать выражение JavaScript, и нет никакого принудительного разделения между шаблоном и данными.
angular
Вместо JSX в Angular имеется собственный "компилятор шаблонов", который принимает строки шаблонов и компилирует их в инструкции JavaScript. Он выполняет ту же роль, что и JSX, но не интегрирован непосредственно в JavaScript. В Solid можно написать JSX-элемент в любом месте, где можно написать выражение JavaScript, и нет принудительного разделения между шаблоном и данными.
JSX + JavaScript¶
JSX становится еще более мощным, если использовать его вместе с выражениями JavaScript. Например, мы можем использовать переменные для представления некоторого текстового содержимого в нашем <div>
:
1 2 3 4 5 |
|
Обратите внимание, как мы можем добавить {name}
туда, где раньше было написано World
. Это выражение JavaScript позволяет нам изменять содержимое нашего <div>
:
Мы можем пойти еще дальше и задать атрибут {style}
нашего div как объект JavaScript:
1 2 3 4 5 6 7 |
|
Благодаря JSX мы получаем код, позволяющий использовать динамическую природу JavaScript в сочетании с декларативным HTML-подобным синтаксисом.
По мере изучения этих уроков мы узнаем, как реактивная система Solid в сочетании с JSX улучшает работу по созданию пользовательских интерфейсов.
vue
Этот синтаксис связывания HTML и значений атрибутов - два разных синтаксиса в Vue. В Vue это может выглядеть следующим образом:
1 |
|
Давайте обновим наш исходный компонент HelloWorld
с учетом того, что мы узнали в ходе изучения JSX:
1 2 3 4 5 6 7 8 |
|
Использование фрагментов¶
Одно из правил при использовании JSX заключается в том, что компонентная функция должна возвращать один элемент. Например, следующий пример является правильным JSX, поскольку в нем есть единственный элемент <div />
, который содержит остальную часть JSX:
1 2 3 4 |
|
Но следующий вариант - это не корректный JSX, поскольку в нем нет ни одного содержащего элемента:
1 2 |
|
Однако, возможно, мы не хотим, чтобы при выводе кода в DOM его окружало div
. JSX-фрагменты дают нам возможность писать корректный JSX и при этом не выводить в DOM содержащий его элемент:
1 2 3 4 |
|
vue
Аналогичным образом работал и Vue 2: компонент должен был иметь один корневой элемент.
angular
Angular имеет схожий с Fragments API с ng-container
.
JSX компилируется¶
JSX представляет собой синтаксис поверх JavaScript и не является непосредственной частью какого-либо стандарта JavaScript. Поэтому JSX не может быть запущен непосредственно в браузере (или в большинстве других программ выполнения JavaScript). Solid и другие фреймворки, использующие JSX, требуют, чтобы их код выполнялся через компилятор. Если вы используете стартовый шаблон, то вам не нужно беспокоиться об этом, все будет работать из коробки.
Помните, мы говорили о том, что имена функций компонентов приводятся в заголовке? Это сделано для того, чтобы компилятор понимал разницу между собственными элементами DOM (например, div
) и пользовательскими компонентами (например, HelloWorld
).
Подробнее о компиляторе
JSX-компилятор Solid не просто компилирует JSX в JavaScript, он также извлекает реактивные значения (о чем мы поговорим позже) и делает все более эффективным.
Это более сложная задача, чем JSX-компилятор React, но гораздо менее сложная, чем, например, компилятор Svelte. Компилятор Solid не трогает JavaScript, а только JSX.
Если вам интересно узнать, как Solid и другие фреймворки используют компиляцию, посмотрите статью A Look at Compilation in JavaScript Frameworks, написанную создателем Solid.
Монтирование Solid в DOM¶
Как наш компонент App
попадает на реальную HTML-страницу, которую мы показываем нашим пользователям?
Чтобы добавить приложение Solid в HTML-документ, мы сначала выделяем в HTML содержащий его элемент и присваиваем ему id
. Обычно это выглядит следующим образом:
1 2 3 |
|
Затем мы вызываем функцию Solid render
, передавая в нее наш компонент App
и данный элемент:
1 |
|
vue
Это аналогично функции Vue app.mount
.
Посмотрите, как это выглядит в нашем проекте в файле index.html
, который создает div, и в файле src/index.tsx
, который вызывает эту функцию render
:
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 6 7 8 9 10 |
|
1 2 3 4 5 6 7 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Необходимо убедиться, что первым аргументом, передаваемым функции render
, является функция, возвращающая компонент () => <Component />
, а не просто сам компонент.
react
В React по умолчанию в корень приложения добавлен компонент StrictMode
, который изменяет поведение рендеринга кода в зависимости от того, находитесь ли вы в режиме разработки или нет.
В Solid нет таких различий между рендерингом в режиме разработки и в режиме производства.
Объединение нескольких компонентов¶
В большинстве случаев наши приложения достаточно велики, чтобы мы захотели разделить их на множество компонентов. Давайте перейдем от примера с HelloWorld
к созданию чего-то более масштабного — виртуальной книжной полки. Наша книжная полка позволит нам добавлять книги, отмечать их как "прочитанные" и, в конечном счете, получать названия книг из других сервисов в Интернете.
Учитывая интуитивную природу JSX, мы можем немного спланировать наше приложение. Наша главная Bookshelf
будет состоять из двух компонентов верхнего уровня:
- компонент
<BookList />
, в котором перечислены все книги, имеющиеся на нашей книжной полке - компонент
<AddBook />
, который позволит нам добавить книгу на книжную полку.
Хотя со временем мы захотим сделать нашу книжную полку интерактивной, давайте сначала создадим неинтерактивные версии каждого компонента. Во-первых, мы можем сделать компонент BookList
внутренним и перечислить несколько книг вместе с их авторами:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Далее мы создаем компонент AddBook
. Это будет форма, которая позволит нам добавить книгу в список BookList
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Finally, we can compose our BookList
and AddBook
components together. Let's create a Bookshelf
component within App.tsx
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Отношения между компонентами¶
Если компонент включает в себя другой компонент, мы называем его родительским компонентом. В нашем примере Bookshelf
компонент Bookshelf
является родительским для BookList
и AddBook
, поскольку эти два компонента находятся внутри Bookshelf
. Соответственно, BookList
и AddBook
являются дочерними компонентами Bookshelf
. Возможно, вы уже знакомы с этой терминологией; так мы обычно обозначаем отношения между HTML-элементами в нашем DOM-дереве.
Передача свойств между компонентами¶
Компоненты могут взаимодействовать со своими дочерними компонентами путем передачи свойств, часто сокращенно называемых props
. Чтобы продемонстрировать возможности свойства props
, давайте настроим книжную полку, добавив ей имя. Для этого добавьте в функцию bookshelf параметр props
. Затем добавим атрибут name
при использовании компонента:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Функции компонентов принимают только один аргумент, если таковой имеется: объект свойства. Свойства этого объекта устанавливаются при использовании компонента и предоставляют атрибуты.
react
В React при обращении к свойствам внутри компонента принято использовать деструктурирующее присваивание. Например, наш компонент Bookshelf
может быть написан в React следующим образом:
1 2 3 4 5 6 7 8 9 |
|
Но деструктуризация свойств обычно является плохой идеей в Solid. Под капотом Solid использует прокси для хука объектов свойств
, чтобы знать, когда к ним обращаются. Когда мы деструктурируем наш объект свойства в сигнатуре функции, мы немедленно получаем доступ к свойствам объекта и теряем реактивность.
Прогресс!¶
Итак, мы познакомились с компонентами Solid, JSX и создали основу для удобного приложения "Книжная полка". Далее мы воплотим нашу книжную полку в жизнь, используя реактивную систему Solid!