Твоё первое SPA на React: основные концепции и разработка
Разбираемся в базовых концепциях SPA и пишем первое одностраничное приложение на React.js.
React.js – замечательный фреймворк для фронтенд-разработки. У него удобный интерфейс и большие возможности, которые лучше всего реализуются при создании одностраничных приложений. Сегодня мы запилим небольшое Single Page Application и параллельно разберёмся в некоторых принципах React.
MPA vs SPA
Если в 2020 году вы ещё не знаете, чем SPA отличается от обычных многостраничных сайтов, пора исправить недоразумение. Если знаете – пролистайте раздел и переходите к практике.
MPA. Возьмём среднестатистический сайт, например, воображаемый интернет-магазин плюшевых игрушек MimimiShop. Вы заходите на главную страницу mimimi-shop.ru и видите много-много карточек с мягкими мишками. Чтобы вы смогли их увидеть, браузер послал запрос на сервер и получил файл index.php
.
Один мишка очень вам нравится, и вы по нему кликаете. Ссылка меняется на mimimi-shop.ru/teddy-bears/92, страница перезагружается, браузер получает другой файл – product.php
. Здесь совершенно другая информация – мишка только один, зато подробно описан.
В классическом Multi Page Application каждый url – это отдельный запрос к серверу с получением нового шаблона.
SPA. Перепишем MimimiShop как SPA. Вы заходите на главную страницу mimimi-shop.ru, и браузер загружает всего один файл index.html
и большой скрипт mimimi.spa.js
. После загрузки скрипт выводит на страницу компонент каталога – множество карточек с мягкими игрушками. Когда вы кликаете на понравившегося мишку, компонент каталога реактивно заменяется на компонент одного товара. Страница при этом не перезагружается, всё происходит красиво и плавно.
В итоге браузер отдал только одну страницу – index.html
.
Сложно ли сделать SPA?
Несложно. Если вы используете инструменты из экосистемы React, то большая часть рутинной работы уже сделана за вас. Сосредоточьтесь на логике приложения, а скучное обновление DOM-дерева оставьте библиотеке.
Что нужно знать? Чтобы разобраться в React, достаточно знать на базовом уровне JavaScript версии ES6. Если неуверены в скиллах, то чекните их в нашей статье Больше JS, чем React: как фреймворк использует возможности языка.
Основы работы React
DOM. Загружаясь на страницу, скрипт берёт под контроль DOM-элемент, который вы ему предоставили. Теперь внутри этого элемента безраздельно властвует React. Он может отслеживать действия пользователя и менять дерево элементов на любом уровне. Для этого используется быстрый виртуальный DOM. В этот элемент может выводиться любой компонент (или дерево компонентов) в зависимости от текущего состояния приложения. Могут меняться какие-то мелочи или даже весь контент целиком.
JSX. Привычной MVC-модели в React нет, так как в компоненте объединяется и представление (рендер), и логика. Это значит, что в компоненте, например, селекта, вы определяете и его внешний вид, и поведение. Для удобства используется специфический JSX-синтаксис (JavaScript XML). Он похож на HTML, но на самом деле это чистой воды JavaScript. Убедиться в этом и поиграться вы можете в онлайн-компиляторе Babel.
Мы не будем подробно разбираться в тонкостях JSX. Если вы с ним незнакомы, то загляните сначала в Подробное руководство по JSX в React, а потом продолжайте чтение.
Запуск проекта
Настройка webpack. Настраивать всю среду разработки с чистого листа было бы долго и скучно. Воспользуемся готовым решением – утилитой create-react-app. Она подготовит начальную структуру проекта, скачает необходимые зависимости и настроит webpack. Если вы плохо знакомы с принципами сборки проекта, загляните во вводное руководство по webpack (и перестаньте уже его бояться).
Откройте рабочую директорию, запустите терминал – начинаем работать.
Устанавливаем утилиту:
Создаём проект:
В директории появилась новая папка react-spa
. Заходим в папку, запускаем команду:
Запустится сервер для локальной разработки, и в браузере откроется новая вкладка с текущим проектом.
Исходники. Все интересующие исходники лежат в папке src
:
src/index.js
– входная точка сборки. Здесь указывается корневой DOM-элемент, в который помещается главный компонентApp
, представляющий собой наше React-приложение.src/App.js
– это собственно код компонентаApp
.
А другие файлы? Помимо этих файлов, в папке src
лежат стили, тесты, несколько служебных скриптов и svg-файл логотипа – всё это нам сейчас неважно. Основные боевые действия будут разворачиваться внутри App.js
.
В документации React подробно описано, что такое компонент и как с ним работать. Нам же сейчас достаточно понимать, что компонент – просто функция, которая принимает извне некоторые свойства (props
), а возвращает разметку для рендера в формате JSX.
Первая страница
Наше SPA будет представлять собой портфолио React-разработчика.
Откройте файл src/App.js
, удалите всё ненужное, и разместите следующий код:
Стили можете написать свои или взять готовые:
Используя JSX-синтаксис и удобные фичи типа циклов, мы создаём разметку простой страницы с шапкой, блоком приветствия и списком выполненных проектов.
На что обратить внимание:
- Вместо атрибута
class
используетсяclassName
; - Все теги, включая
img
, должны быть закрыты; - Для вывода массива элементов используется метод
map
; - Каждый элемент массива, создаваемого методом
map
, должен иметь уникальный ключkey
.
Структура проекта и компоненты
Заботимся о структуре. Если продолжить писать всё в одном файле, SPA очень быстро разрастётся, и мы запутаемся в коде. Нужно разделить проект на отдельные компоненты.
Сейчас у нас наметились несколько самостоятельных блоков:
Header
– шапка с брендом;About
– блок с приветствием;PortfolioItem
– карточка одного проекта.
Создадим директорию src/components
, а внутри – отдельные папки для каждого компонента. Код и стили блоком переносим практически без изменений:
Подключаем компоненты внутри App:
Разметка. Каждый компонент создаёт определённую разметку и заполняет её данными, полученными из родительского компонента через props
. Обратите внимание, что компонент может получать также вложенную в него разметку – через свойство props.children
.
Что изменилось? В качестве уникального ключа для элементов массива мы теперь используем id проекта, а не порядковый индекс элемента. Эта практика лучше, так как позволяет React оптимизировать рендер.
Директория src
теперь выглядит так:
Все исходники можно посмотреть здесь. Визуально ничего не изменилось.
Чем больше проект, тем важнее становится его структура. Загляните в большое руководство по структурированию react-проектов, чтобы создавать удобные и легко поддерживаемые приложения.
Взаимодействие с пользователем
Добавляем формы. Теперь нужно научить наше SPA отслеживать действия пользователя и реагировать на них. Для демонстрации лучше всего подходят формы, поэтому мы добавим форму обратной связи в наше портфолио. Вот так это будет выглядеть на странице.
Форма обратной связи. Добавляем новый компонент ContactForm
.
Этот компонент имеет состояние: он хранит значения двух полей, а также флаги, указывающие, нужно ли показывать ошибку валидации. Поэтому при его создании мы используем класс, наследующий от React.Component
. Весь рендер при этом переносится в метод render
.
Обработчики событий. У ContactForm
есть также методы-обработчики событий onSubmit
(отправка формы) и onChange
(изменение значений полей). Поля ввода полностью контролируются React – мы отслеживаем ввод и указываем, что выводить. Чтобы лучше понять, что такое контролируемые компоненты и как происходит изменение данных в формах, загляните в документацию React.
Храним состояние формы. Теперь у компонента App появляется состояние: форма может быть закрытой или открытой. Чтобы хранить этот параметр переделаем App из функционального компонента (stateless) в классовый и добавим ему поле state
.
Внизу разместим кнопку, клики по которой будем отслеживать с помощью атрибута onClick
.
Все исходники здесь.
Обратите внимание: Для изменения состояния компонента предназначен метод this.setState
, который принимает новый объект состояния. Подробнее о состоянии вы можете прочитать в документации.
Страница проекта
Добавим в приложение страницы. Хоть мы и используем React, до сих пор в нашем проекте не было ничего особенного. Мы работаем в пределах одной страницы, отслеживаем события и меняем DOM. Чтобы прикоснуться к магии Single Page Application, нужно в наше приложение добавить страниц. Например, было бы неплохо показывать детальную информацию о выполненном проекте по адресу /projects/{идентификатор_проекта}
.
Требования к маршрутизации SPA:
- Переход между страницами должен происходить без перезагрузки.
- Каждый переход должен сохраняться в истории браузера (кнопки Назад/Вперед должны функционировать).
- При вводе в адресную строку некоторого маршрута должен выводиться тот контент, который этому маршруту соответствует, как в мультистраничном приложении.
Пакет react-router-dom. Не переживайте, нам не придётся заниматься этим вручную. Честно говоря, нам вообще мало что придётся делать – всё уже сделано. Пакет react-router инкапсулирует логику маршрутизации, а react-router-dom обеспечивает его взаимодействие с DOM в браузере.
Устанавливаем модуль:
Теперь откройте файл src/index.js
и оберните всё приложение в компонент BrowserRouter
.
Страница проекта будет иметь тот же хедер, что и главная, но другой контент. Чтобы логически разделить их, создадим в проекте директорию src/pages
с двумя папками: home
и project
. В home/index.js
перенесем имеющийся код из компонента App
, а в project/index.js
создадим разметку для новой страницы проекта.
Сами маршруты мы определим прямо в компоненте App, но в реальном приложении, вы, вероятно, захотите указать их в отдельном файле для удобства.
Все исходники вы можете найти здесь. Демонстрационная версия приложения имеет ограниченную функциональность – нет редиректа на главную при вводе несуществующего маршрута.
На что обратить внимание:
- Идентификатор конкретного проекта передается прямо в url как параметр
:id
. - Извлечь его в компоненте можно из
this.props.match.params.id
. - В методе
componentDidMount
компонентаProjectPage
можно сделать запрос к серверу для получения данных проекта. В коде задержку запроса имитирует методsetTimeout
. - Все внутренние ссылки (в компонентах
Header
иProjectPreview
) заменены на компонентLink
, это обеспечивает переход между маршрутами без перезагрузки. - Компонент
App
больше не имеет состояния – оно перешло в компонентHomePage
– поэтому его снова можно сделать функциональным.
Чтобы глубже разобраться в работе роутера, загляните в этот туториал по использованию React Router.
Резюмируем: основы создания SPA
Итак, что требуется для создания Single Page Application на React?
- Разделить код проекта на компоненты, которые можно заменять.
- Отслеживать действия пользователя для изменения состояния приложения.
- Настроить маршрутизацию без перезагрузки, передавая при необходимости параметры маршрутам для более тонкой настройки.
Ваше первое простое SPA на React готово!
Не останавливайтесь на достигнутом, продолжайте развиваться, учиться и создавать самые крутые приложения.