Remix
Remix - фреймворк для создания клиент-серверных веб-приложений на JavaScript (React) со встроенной поддержкой TypeScript.
Remix позволяет разрабатывать так называемые PESPA (Progressive Enhancement Single Page Apps - одностраничные приложения с возможностью прогрессивного улучшения). Это означает следующее:
- почти весь код приложения "живет" на сервере;
- приложение остается функциональным даже при отсутствии JS;
- JS используется только для прогрессивного улучшения UX (User Experience - пользовательский опыт).
Подробнее о PESPA и других архитектурах веб-приложений можно почитать здесь.
Маршрутизация / Routing
Маршрутизация (роутинг) - это, пожалуй, самая важная концепция в Remix. Все начинается с маршрутов (роутов): компилятор, первоначальный запрос документа и обработка почти всех последующих действий пользователя.
Начнем с определения понятий:
- вложенные роуты (nested routes) - связь (map) роутов с сегментами URL обеспечивает соответствие URL определенным компонентам и данным, известным перед рендерингом страницы;
- URL - полный путь в поисковой строке браузера пользователя. Один URL может совпадать (соответствовать - match) с несколькими роутами. Обратите внимание: роут и URL - разные вещи в Remix;
- роут (route) или модуль роута (route module) - модуль JS с определенными экспортами (
loader
,action
,default function
(компонент) и др.), который соответствует одному или нескольким сегментам URL. Поскольку роут соответствует сегменту URL, по одному пути могут рендерится несколько модулей. Иерархия компонентов соответствует сегментам URL (в основном); - путь (path) или путь роута (route path) - сегмент URL, которому соответствует отдельный модуль. Путь роута определяется названием файла в директории
app/routes
; - родительский макет (parent layout route) или родительский роут (parent route) - модуль, который рендерит макет для дочерних компонентов через компонент
Outlet
; - макет без пути (pathless layout route) или роут без пути (pathless route) - модуль, который не добавляет сегменты к URL, но добавляет компонент в иерархию UI (User Interface - пользовательский интерфейс) при совпадении его дочерних роутов;
- дочерний роут (child route) - модуль, который рендерится внутри родительского
Outlet
при совпадении его пути с URL; - индексный роут (index route) - модуль, который имеет та кой же путь, как его родительский роут, но рендерится в качестве дефолтного дочернего роута внутри
Outlet
; - динамический сегмент (dynamic segment) - сегмент пути роута, извлекаемый из URL и передаваемый в приложение, такой как идентификатор (ID) записи или слаг (slug) поста;
- сплат (splat) - замыкающая звездочка (trailing wildcard) в пути роута, который благодаря этому совпадает со всеми сегментами URL (включая последующий
/
); - аутлет (outlet) - компонент, который рендерится внутри родительского модуля, предназначенный для рендеринга дочерних модулей. Другими словами, аутлет определяет локацию дочерних роутов.
Вложенный роутинг
Вложенный роутинг - это связь между сегментами URL и иерархией компонентов в UI. Сегменты URL определяют:
- макеты, формирующие страницу;
- загрузку JS-кода, используемого на странице;
- загрузку данных, используемых на странице.
Определение роутов
Роуты определяются посредством создания файлов в директ ории app/routes
. Вот как может выглядеть иерархия роутов приложения:
app
├── root.jsx
└── routes
├── accounts.jsx
├── dashboard.jsx
├── expenses.jsx
├── index.jsx
├── reports.jsx
├── sales
│ ├── customers.jsx
│ ├── deposits.jsx
│ ├── index.jsx
│ ├── invoices
│ │ ├── $invoiceId.jsx
│ │ └── index.jsx
│ ├── invoices.jsx
│ └── subscriptions.jsx
└── sales.jsx
root.jsx
- это корневой роут, служащий макетом для всего приложения. Другие роуты рендерятся внутри егоOutlet
;- обратите внимание на файлы, названия которых совпадают с названиями директорий, в которых эти файлы находятся. Эти файлы предназначены для формирования иерархии макетов компонентов. Например,
sales.jsx
- это родительский роут для всех дочерних роутов внутри директорииapp/routes/sales
. При совпадении с URL любого роута из этой директории, он будет рендерится внутриOutlet
модуляsales.jsx
; - роут
index.jsx
будет рендерится внутриOutlet
при совпадении URL с путем директории (например, путиexample.com/sales
соответствует роутapp/routes/sales/index.jsx
).
Рендеринг иерархии макета роута
Предположим, что URL имеет вид /sales/invoices/123
. С этим URL будут совпадать следующие роуты:
root.jsx
;routes/sales.jsx
;routes/sales/invoices.jsx
;routes/sales/invoices/$invoiceId.jsx
.
При посещении этой страницы пользователем Remix отрендерит такую иерархию компонентов:
<Root>
<Sales>
<Invoices>
<InvoiceId />
</Invoices>
</Sales>
</Root>
Иерархия компонентов полностью соответствует иерархии файлов в директории app/routes
:
app
├── root.jsx
└── routes
├── sales
│ ├── invoices
│ │ └── $invoiceId.jsx
│ └── invoices.jsx
├── sales.jsx
└── accounts.jsx
Иерархия компонентов для URL /accounts
будет такой:
<Root>
<Accounts />
</Root>
Для рендеринга дочерних роутов внутри родительского используется Outlet
. root.jsx
рендерит основной макет, боковую панель и аутлет для дочерних роутов:
import { Outlet } from "@remix-run/react";
export default function Root() {
return (
<Document>
<Sidebar />
{/* ! */}
<Outlet />
</Document>
);
}