Компоновки, шаблоны и группы маршрутов
Оглавление курса · Скачать все состояния проекта.
Учебная задача: Сохранить общую рабочую оболочку без изменения публичных адресов.
Входное состояние: Есть отдельные страницы списка и карточки. Выходное состояние: Рабочие страницы используют одну шапку и группу маршрутов. Продолжаем «Смену» — учебную диспетчерскую заявок мастерской. Нужны основы JavaScript, TypeScript, React и HTTP. Используем App Router, Next 16.4.0, React 19.3.0 и Node.js не ниже 20.9.0. Основные версии закреплены; диапазоны остальных пакетов ещё не разрешены установкой и lockfile отсутствует. Полный каталог lesson-03 в архиве соответствует окончанию этой главы. Выбирайте один снимок целиком: перенос отдельных файлов из будущих этапов меняет его зависимости и правила данных.
Исходники подготовлены редакционным чтением. Пакеты, сервер, сборка, миграции, примеры и внешнее размещение не выполнялись. Ответы страницы и изменения данных ниже являются ожидаемыми, example_execution: not_run.
Список и карточка принадлежат одной рабочей области. Диспетчеру удобно видеть общую шапку со ссылками, независимо от выбранной заявки. Если скопировать шапку в каждую страницу, её изменение потребует нескольких правок. Вынесем оболочку в компоновку и сохраним существующие адреса. Для организации файлов используем группу (work), которая не добавляет буквальный сегмент в URL.
Переносим каталог tickets внутрь app/(work). Публичный путь остаётся /tickets, а не /work/tickets. Скобки сообщают маршрутизатору, что имя служит организацией исходников. В архиве старое дерево убрано, поэтому нет двух страниц, претендующих на один адрес. При самостоятельной правке важно именно перенести файлы, а не оставить одновременно две копии маршрута.
Компоновка принимает выбранную страницу
WorkLayout возвращает шапку и children. В зависимости от URL в это место попадёт список или карточка. Корневая компоновка по-прежнему создаёт html и body, поэтому рабочая оболочка не повторяет их. Эти два уровня выполняют разные задачи: верхний описывает документ, нижний организует конкретный раздел приложения. Вложенная компоновка расширяет общую структуру, а не отменяет её.
При обычной клиентской навигации общая компоновка сохраняется между дочерними страницами. Это позволяет сохранять расположенное в ней состояние клиентских элементов. Не следует понимать сохранение как запрет любых обновлений или обещание одинаковых данных навсегда. Если оболочка зависит от пользователя, выбранный механизм получения личности и обновления должен учитывать эту задачу отдельно. На текущем шаге шапка содержит только общие ссылки.
Название layout.tsx является частью файлового договора Next. Файл с названием work-layout.tsx может быть обычным импортируемым компонентом, но маршрутизатор не подключит его сам. Поэтому полезно сначала видеть, какой уровень выбирается автоматически, а затем читать обычные импорты внутри него. Предметные компоненты можно называть свободно; специальные файлы выполняют роли, определённые фреймворком.
Чем шаблон отличается от компоновки
Добавим template.tsx с семантической секцией рабочей области. Шаблон похож по входу на компоновку, но при навигации создаёт новый экземпляр с новым ключом. Его клиентское состояние и эффекты могут начинаться заново. Это удобно, когда экрану требуется новый жизненный цикл, но не означает, что любой общий элемент нужно помещать туда. Шапка остаётся в компоновке именно потому, что её роль стабильна.
В этой главе шаблон не содержит анимации и не притворяется, что переход уже проверен визуально. Он показывает место для поведения, которому нужна новая инициализация. Если позже добавить в него клиентский счётчик или эффект, следует ожидать иной жизненный цикл, чем у элемента общей компоновки. Сам CSS не объясняет это различие: дело в том, как маршрутизатор создаёт и сохраняет части дерева React.
Группы помогают разделить публичную и рабочую части по исходникам. Однако имя (work) не является проверкой входа. Пока любой посетитель может открыть учебный список. Для защиты нужны серверные проверки личности и полномочий, которые появятся в следующих этапах. Скрытый организационный сегмент не делает URL неизвестным и не препятствует прямому запросу к нему.
Несколько корневых оболочек
Next допускает архитектуру с разными корневыми компоновками через группы, но это уже другой договор переходов. Между разными корнями возможна полная загрузка документа. В нашей «Смене» сохраняется один корневой layout.tsx, а рабочая группа добавляет вложенный уровень. Это достаточная модель для сайта мастерской и не требует двух независимых HTML-документов ради одного меню.
Также нельзя назначить одинаковый конечный URL двум группам и считать, что скобки помогут выбрать одну. Поскольку имена групп не участвуют в адресе, две одинаковые страницы окажутся конфликтом маршрута. Эта особенность важна при переносе файлов: визуально каталоги различаются, а публичный договор может совпадать. Проверяйте итоговый адрес, а не только полный путь исходника на диске.
Ожидаемое состояние после переноса простое: главная страница открывается без рабочей шапки, а список и карточка используют её вместе. Ссылка на руководство пока ведёт в отдельную ветку, где эта компоновка не применяется. Наличие ссылки в шапке не помещает целевую страницу внутрь группы. Компоновка определяется деревом её собственного маршрута, а не местом, откуда пользователь пришёл.
Для самостоятельного разбора проследите вложение корня, рабочей компоновки, шаблона и карточки. Затем решите, куда поместить элемент, который должен сохранять выбранное значение при переходе между заявками, а куда элемент, который должен начинаться заново. Объясняйте решение через жизненный цикл и ответственность, а не через длину файла. Это подготовит нас к теме, входу и параллельным областям интерфейса.
Проверьте смысл границ
Сохранение состояния компоновки становится особенно заметным, когда в шапке появится клиентский элемент. Пока он не перемонтируется при смене дочерней страницы, выбранное значение может продолжать действовать. Шаблон подходит другому намерению: новый экран получает новый жизненный цикл. Выберите поведение до того, как переносить компонент ради сокращения файла.
Если руководство должно иметь ту же шапку, его дерево надо включить в рабочую группу либо использовать общий компонент в иной компоновке. Простое добавление ссылки в меню не меняет принадлежность целевого маршрута. Это хороший пример различия композиции документа и графа переходов между страницами: оба видны пользователю, но описываются разными участками исходника.
Полные файлы этого шага
Следующие файлы показаны полностью. Остальные согласованные компоненты и настройки находятся в том же каталоге архива; сохраняйте их вместе при самостоятельном прохождении.
app/layout.tsx
import "./globals.css";
import type { ReactNode } from "react";
export default function RootLayout({ children }: { children: ReactNode }) {
return <html lang="ru"><body>{children}</body></html>;
}
app/(work)/layout.tsx
import Link from "next/link";
import type { ReactNode } from "react";
export default function WorkLayout({ children }: { children: ReactNode }) {
return <><header><Link href="/">Смена</Link><Link href="/tickets">Заявки</Link><Link href="/guide">Руководство</Link></header>{children}</>;
}
app/(work)/template.tsx
import type { ReactNode } from "react";
export default function WorkTemplate({ children }: { children: ReactNode }) {
return <section aria-label="Рабочая область">{children}</section>;
}
Документация и исходники
Договор этой главы раскрыт в официальной документации. Сопоставьте её API с выбранной версией и с объяснённым источником каждого значения. Архив «Смены» содержит двадцать полных состояний и условия самостоятельной подготовки. Ожидаемый результат оценивается по текущей главе: поздние проверки владельца, база и внешние ключи не являются неявно выполненными предпосылками раннего учебного состояния.