Переход с Pages Router на App Router
Это руководство поможет вам:
- Обновить приложение Next.js с версии 12 до версии 13
- Обновить функции, работающие как в директории
pages, так и вapp - Постепенно перенести существующее приложение с
pagesнаapp
Обновление
Версия Node.js
Минимальная версия Node.js теперь v16.14. Подробнее см. в документации Node.js.
Версия Next.js
Для обновления до Next.js версии 13 выполните следующую команду с помощью предпочитаемого менеджера пакетов:
npm install next@latest react@latest react-dom@latestВерсия ESLint
Если вы используете ESLint, вам нужно обновить его версию:
npm install -D eslint-config-next@latestПолезно знать: Возможно, вам потребуется перезапустить сервер ESLint в VS Code, чтобы изменения вступили в силу. Откройте палитру команд (
cmd+shift+pна Mac;ctrl+shift+pна Windows) и найдитеESLint: Restart ESLint Server.
Следующие шаги
После обновления ознакомьтесь со следующими разделами:
- Обновление новых функций: Руководство по переходу на новые функции, такие как улучшенные компоненты Image и Link.
- Миграция с
pagesнаapp: Пошаговое руководство по постепенному переходу с директорииpagesнаapp.
Обновление новых функций
Next.js 13 представил новый App Router с новыми функциями и соглашениями. Новый роутер доступен в директории app и сосуществует с директорией pages.
Обновление до Next.js 13 не требует использования нового App Router. Вы можете продолжать использовать pages с новыми функциями, работающими в обеих директориях, такими как обновлённые компонент Image, компонент Link, компонент Script и оптимизация шрифтов.
Компонент <Image/>
Next.js 12 представил улучшения для компонента Image с временным импортом: next/future/image. Эти улучшения включали меньше клиентского JavaScript, более простые способы расширения и стилизации изображений, лучшую доступность и нативную ленивую загрузку в браузере.
В версии 13 это новое поведение стало стандартным для next/image.
Доступны два кодмода для помощи в миграции на новый компонент Image:
- Кодмод
next-image-to-legacy-image: Безопасно и автоматически переименовывает импортыnext/imageвnext/legacy/image. Существующие компоненты сохранят прежнее поведение. - Кодмод
next-image-experimental: Опасный кодмод, добавляющий встроенные стили и удаляющий неиспользуемые пропсы. Это изменит поведение существующих компонентов в соответствии с новыми стандартами. Для использования этого кодмода сначала нужно выполнитьnext-image-to-legacy-image.
Компонент <Link>
Компонент <Link> больше не требует ручного добавления тега <a> как дочернего элемента. Это поведение было добавлено как экспериментальная опция в версии 12.2 и теперь стало стандартным. В Next.js 13 <Link> всегда рендерит <a> и позволяет передавать пропсы вложенному тегу.
Например:
import Link from 'next/link'
// Next.js 12: `<a>` должен быть вложенным, иначе он исключается
<Link href="/about">
<a>About</a>
</Link>
// Next.js 13: `<Link>` всегда рендерит `<a>` под капотом
<Link href="/about">
About
</Link>Для обновления ссылок в Next.js 13 можно использовать кодмод new-link.
Компонент <Script>
Поведение next/script было обновлено для поддержки как pages, так и app, но для плавной миграции необходимо внести некоторые изменения:
- Переместите все скрипты
beforeInteractive, которые ранее включались в_document.js, в корневой файл макета (app/layout.tsx). - Экспериментальная стратегия
workerпока не работает вapp, и скрипты с этой стратегией придётся либо удалить, либо изменить на другую стратегию (например,lazyOnload). - Обработчики
onLoad,onReadyиonErrorне работают в серверных компонентах, поэтому их нужно переместить в клиентский компонент или полностью удалить.
Оптимизация шрифтов
Ранее Next.js помогал оптимизировать шрифты с помощью встраивания CSS шрифтов. Версия 13 представляет новый модуль next/font, который позволяет настраивать процесс загрузки шрифтов, сохраняя при этом высокую производительность и приватность. next/font поддерживается как в директории pages, так и в app.
Хотя встраивание CSS по-прежнему работает в pages, оно не работает в app. Вместо этого следует использовать next/font.
Подробнее об использовании next/font см. на странице Оптимизация шрифтов.
Миграция с pages на app
🎥 Видео: Узнайте, как постепенно внедрять App Router → YouTube (16 минут).
Переход на App Router может быть первым опытом использования функций React, на которых построен Next.js, таких как серверные компоненты, Suspense и другие. В сочетании с новыми функциями Next.js, такими как специальные файлы и макеты, миграция означает изучение новых концепций, моделей мышления и изменений в поведении.
Мы рекомендуем снизить сложность этих обновлений, разбив миграцию на меньшие шаги. Директория app специально разработана для одновременной работы с директорией pages, что позволяет постепенно переносить приложение страница за страницей.
- Директория
appподдерживает вложенные маршруты и макеты. Подробнее. - Используйте вложенные папки для определения маршрутов и специальный файл
page.js, чтобы сделать сегмент маршрута общедоступным. Подробнее. - Специальные файлы используются для создания UI для каждого сегмента маршрута. Наиболее распространённые специальные файлы —
page.jsиlayout.js.- Используйте
page.jsдля определения UI, уникального для маршрута. - Используйте
layout.jsдля определения UI, общего для нескольких маршрутов. - Для специальных файлов можно использовать расширения
.js,.jsxили.tsx.
- Используйте
- В директории
appможно размещать другие файлы, такие как компоненты, стили, тесты и т. д. Подробнее. - Функции получения данных, такие как
getServerSidePropsиgetStaticProps, заменены на новый API внутриapp.getStaticPathsзаменён наgenerateStaticParams. pages/_app.jsиpages/_document.jsзаменены одним корневым макетомapp/layout.js. Подробнее.pages/_error.jsзаменён более детализированными специальными файламиerror.js. Подробнее.pages/404.jsзаменён файломnot-found.js.pages/api/*пока остаются в директорииpages.
Шаг 1: Создание директории app
Обновитесь до последней версии Next.js (требуется 13.4 или выше):
npm install next@latestЗатем создайте новую директорию app в корне вашего проекта (или в директории src/).
Шаг 2: Создание корневого макета
Создайте новый файл app/layout.tsx внутри директории app. Это корневой макет, который будет применяться ко всем маршрутам внутри app.
export default function RootLayout({
// Макеты должны принимать проп children.
// Это будет заполнено вложенными макетами или страницами
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}export default function RootLayout({
// Макеты должны принимать проп children.
// Это будет заполнено вложенными макетами или страницами
children,
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}- Директория
appобязательно должна включать корневой макет. - Корневой макет должен определять теги
<html>и<body>, так как Next.js не создаёт их автоматически. - Корневой макет заменяет файлы
pages/_app.tsxиpages/_document.tsx. - Для файлов макетов можно использовать расширения
.js,.jsxили.tsx.
Для управления HTML-элементами <head> можно использовать встроенную поддержку SEO:
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}export const metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}Миграция _document.js и _app.js
Если у вас есть существующие файлы _app или _document, вы можете скопировать их содержимое (например, глобальные стили) в корневой макет (app/layout.tsx). Стили в app/layout.tsx не будут применяться к pages/*. Вы должны сохранить _app/_document во время миграции, чтобы маршруты pages/* продолжали работать. После полной миграции их можно безопасно удалить.
Если вы используете провайдеры React Context, их нужно переместить в клиентский компонент.
Миграция шаблона getLayout() на макеты (опционально)
Next.js рекомендовал добавлять свойство к компонентам страниц для реализации макетов на уровне страниц в директории pages. Этот шаблон можно заменить нативную поддержку вложенных макетов в директории app.
Пример до и после
До
export default function DashboardLayout({ children }) {
return (
<div>
<h2>My Dashboard</h2>
{children}
</div>
)
}import DashboardLayout from '../components/DashboardLayout'
export default function Page() {
return <p>My Page</p>
}
Page.getLayout = function getLayout(page) {
return <DashboardLayout>{page}</DashboardLayout>
}После
-
Удалите свойство
Page.getLayoutизpages/dashboard/index.jsи следуйте шагам миграции страниц в директориюapp.app/dashboard/page.js export default function Page() { return <p>My Page</p> } -
Переместите содержимое
DashboardLayoutв новый клиентский компонент, чтобы сохранить поведение директорииpages.app/dashboard/DashboardLayout.js 'use client' // Эта директива должна быть вверху файла, перед любыми импортами. // Это клиентский компонент export default function DashboardLayout({ children }) { return ( <div> <h2>My Dashboard</h2> {children} </div> ) } -
Импортируйте
DashboardLayoutв новый файлlayout.jsвнутри директорииapp.app/dashboard/layout.js import DashboardLayout from './DashboardLayout' // Это серверный компонент export default function Layout({ children }) { return <DashboardLayout>{children}</DashboardLayout> } -
Вы можете постепенно перемещать неинтерактивные части
DashboardLayout.js(клиентский компонент) вlayout.js(серверный компонент), чтобы уменьшить количество JavaScript, отправляемого клиенту.
Шаг 3: Миграция next/head
В директории pages React-компонент next/head использовался для управления HTML-элементами <head>, такими как title и meta. В директории app next/head заменён новой встроенной поддержкой SEO.
До:
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}После:
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}export const metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}Шаг 4: Перенос страниц
- Страницы в директории
appпо умолчанию являются Серверными компонентами (Server Components). Это отличается от директорииpages, где страницы являются Клиентскими компонентами (Client Components). - Получение данных (Data fetching) изменилось в
app. МетодыgetServerSideProps,getStaticPropsиgetInitialPropsзаменены на более простой API. - Директория
appиспользует вложенные папки для определения маршрутов (defining routes) и специальный файлpage.js, чтобы сделать сегмент маршрута общедоступным. -
Директория pagesДиректория appМаршрут index.jspage.js/about.jsabout/page.js/aboutblog/[slug].jsblog/[slug]/page.js/blog/post-1
Рекомендуем разбить перенос страницы на два основных шага:
- Шаг 1: Перенести экспортируемый по умолчанию компонент страницы в новый Клиентский компонент.
- Шаг 2: Импортировать новый Клиентский компонент в файл
page.jsвнутри директорииapp.
Полезно знать: Это самый простой путь миграции, так как он наиболее близок к поведению директории
pages.
Шаг 1: Создание нового Клиентского компонента
- Создайте новый отдельный файл внутри директории
app(например,app/home-page.tsxили аналогичный), который экспортирует Клиентский компонент. Чтобы определить Клиентские компоненты, добавьте директиву'use client'в начало файла (перед любыми импортами). - Перенесите экспортируемый по умолчанию компонент страницы из
pages/index.jsвapp/home-page.tsx.
'use client'
// Это Клиентский компонент. Он получает данные через пропсы и
// имеет доступ к состоянию и эффектам, как и компоненты страниц
// в директории `pages`.
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}'use client'
// Это Клиентский компонент. Он получает данные через пропсы и
// имеет доступ к состоянию и эффектам, как и компоненты страниц
// в директории `pages`.
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}Шаг 2: Создание новой страницы
-
Создайте новый файл
app/page.tsxвнутри директорииapp. Это по умолчанию Серверный компонент. -
Импортируйте Клиентский компонент
home-page.tsxв страницу. -
Если вы получали данные в
pages/index.js, перенесите логику получения данных непосредственно в Серверный компонент, используя новый API для получения данных (data fetching APIs). Подробнее см. в руководстве по обновлению получения данных.// Импортируйте ваш Клиентский компонент import HomePage from './home-page' async function getPosts() { const res = await fetch('https://...') const posts = await res.json() return posts } export default async function Page() { // Получайте данные напрямую в Серверном компоненте const recentPosts = await getPosts() // Передавайте полученные данные в ваш Клиентский компонент return <HomePage recentPosts={recentPosts} /> }// Импортируйте ваш Клиентский компонент import HomePage from './home-page' async function getPosts() { const res = await fetch('https://...') const posts = await res.json() return posts } export default async function Page() { // Получайте данные напрямую в Серверном компоненте const recentPosts = await getPosts() // Передавайте полученные данные в ваш Клиентский компонент return <HomePage recentPosts={recentPosts} /> } -
Если ваша предыдущая страница использовала
useRouter, вам нужно обновить её до новых хуков маршрутизации. Подробнее. -
Запустите сервер разработки и откройте
http://localhost:3000. Вы должны увидеть ваш существующий маршрут index, теперь обслуживаемый через директорию app.
Шаг 5: Перенос хуков маршрутизации
Добавлен новый роутер для поддержки нового поведения в директории app.
В app следует использовать три новых хука, импортируемых из next/navigation: useRouter(), usePathname() и useSearchParams().
- Новый хук
useRouterимпортируется изnext/navigationи ведёт себя иначе, чем хукuseRouterвpages, который импортируется изnext/router.- Хук
useRouter, импортируемый изnext/router, не поддерживается в директорииapp, но может продолжать использоваться в директорииpages.
- Хук
- Новый
useRouterне возвращает строкуpathname. Вместо этого используйте отдельный хукusePathname. - Новый
useRouterне возвращает объектquery. Вместо этого используйте отдельный хукuseSearchParams. - Вы можете использовать
useSearchParamsиusePathnameвместе для отслеживания изменений страницы. Подробнее см. в разделе События роутера (Router Events). - Эти новые хуки поддерживаются только в Клиентских компонентах. Они не могут использоваться в Серверных компонентах.
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}Кроме того, новый хук useRouter имеет следующие изменения:
isFallbackудалён, так какfallbackбыл заменён.- Значения
locale,locales,defaultLocales,domainLocalesудалены, так как встроенные функции i18n Next.js больше не нужны в директорииapp. Подробнее о i18n. basePathудалён. Альтернатива не будет частьюuseRouter. Она ещё не реализована.asPathудалён, так как концепцияasбыла удалена из нового роутера.isReadyудалён, так как больше не нужен. Во время статического рендеринга (static rendering) любой компонент, использующий хукuseSearchParams(), пропускает этап предварительного рендеринга и вместо этого рендерится на клиенте во время выполнения.
См. справочник API useRouter().
Шаг 6: Перенос методов получения данных
В директории pages используются getServerSideProps и getStaticProps для получения данных для страниц. В директории app эти методы заменены на более простой API, построенный на основе fetch() и асинхронных Серверных компонентов React.
export default async function Page() {
// Этот запрос должен кэшироваться до ручной инвалидации.
// Аналогично `getStaticProps`.
// `force-cache` установлен по умолчанию и может быть опущен.
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// Этот запрос должен перезапрашиваться при каждом запросе.
// Аналогично `getServerSideProps`.
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// Этот запрос должен кэшироваться с временем жизни 10 секунд.
// Аналогично `getStaticProps` с опцией `revalidate`.
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}export default async function Page() {
// Этот запрос должен кэшироваться до ручной инвалидации.
// Аналогично `getStaticProps`.
// `force-cache` установлен по умолчанию и может быть опущен.
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// Этот запрос должен перезапрашиваться при каждом запросе.
// Аналогично `getServerSideProps`.
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// Этот запрос должен кэшироваться с временем жизни 10 секунд.
// Аналогично `getStaticProps` с опцией `revalidate`.
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}Рендеринг на стороне сервера (getServerSideProps)
В директории pages getServerSideProps используется для получения данных на сервере и передачи пропсов в экспортируемый по умолчанию React-компонент в файле. Начальный HTML для страницы предварительно рендерится на сервере, после чего страница "гидратируется" в браузере (становится интерактивной).
// Директория `pages`
export async function getServerSideProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Dashboard({ projects }) {
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}В директории app мы можем разместить получение данных внутри наших React-компонентов, используя Серверные компоненты (Server Components). Это позволяет отправлять меньше JavaScript на клиент, сохраняя при этом HTML, отрендеренный на сервере.
Установив опцию cache в no-store, мы можем указать, что полученные данные никогда не должны кэшироваться. Это аналогично getServerSideProps в директории pages.
// Директория `app`
// Эта функция может называться как угодно
async function getProjects() {
const res = await fetch(`https://...`, { cache: 'no-store' })
const projects = await res.json()
return projects
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}// Директория `app`
// Эта функция может называться как угодно
async function getProjects() {
const res = await fetch(`https://...`, { cache: 'no-store' })
const projects = await res.json()
return projects
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}Доступ к объекту запроса
В директории pages вы можете получить данные запроса на основе Node.js HTTP API.
Например, вы можете получить объект req из getServerSideProps и использовать его для получения cookies и заголовков запроса.
// Директория `pages`
export async function getServerSideProps({ req, query }) {
const authHeader = req.getHeaders()['authorization'];
const theme = req.cookies['theme'];
return { props: { ... }}
}
export default function Page(props) {
return ...
}Директория app предоставляет новые функции только для чтения для получения данных запроса:
headers(): Основана на Web Headers API и может использоваться внутри Серверных компонентов (Server Components) для получения заголовков запроса.cookies(): Основана на Web Cookies API и может использоваться внутри Серверных компонентов (Server Components) для получения cookies.
// Директория `app`
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = headers().get('authorization')
return '...'
}
export default async function Page() {
// Вы можете использовать `cookies()` или `headers()` внутри Серверных компонентов
// напрямую или в вашей функции получения данных
const theme = cookies().get('theme')
const data = await getData()
return '...'
}// Директория `app`
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = headers().get('authorization')
return '...'
}
export default async function Page() {
// Вы можете использовать `cookies()` или `headers()` внутри Серверных компонентов
// напрямую или в вашей функции получения данных
const theme = cookies().get('theme')
const data = await getData()
return '...'
}Статическая генерация сайта (getStaticProps)
В директории pages функция getStaticProps используется для предварительного рендеринга страницы во время сборки. Эта функция может использоваться для получения данных из внешнего API или непосредственно из базы данных и передачи этих данных на всю страницу во время её генерации при сборке.
// Директория `pages`
export async function getStaticProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Index({ projects }) {
return projects.map((project) => <div>{project.name}</div>)
}В директории app получение данных с помощью fetch() по умолчанию использует cache: 'force-cache', что кэширует данные запроса до ручной инвалидации. Это аналогично getStaticProps в директории pages.
// Директория `app`
// Эта функция может называться как угодно
async function getProjects() {
const res = await fetch(`https://...`)
const projects = await res.json()
return projects
}
export default async function Index() {
const projects = await getProjects()
return projects.map((project) => <div>{project.name}</div>)
}Динамические пути (getStaticPaths)
В директории pages функция getStaticPaths используется для определения динамических путей, которые должны быть предварительно отрендерены во время сборки.
// Директория `pages`
import PostLayout from '@/components/post-layout'
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
}
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return { props: { post } }
}
export default function Post({ post }) {
return <PostLayout post={post} />
}В директории app функция getStaticPaths заменена на generateStaticParams.
Функция generateStaticParams работает аналогично getStaticPaths, но имеет упрощенный API для возврата параметров маршрута и может использоваться внутри макетов (layouts). В отличие от getStaticPaths, которая возвращает массив вложенных объектов param или строку с готовыми путями, generateStaticParams возвращает массив сегментов.
// Директория `app`
import PostLayout from '@/components/post-layout'
export async function generateStaticParams() {
return [{ id: '1' }, { id: '2' }]
}
async function getPost(params) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return post
}
export default async function Post({ params }) {
const post = await getPost(params)
return <PostLayout post={post} />
}Название generateStaticParams более подходит для новой модели в директории app, чем getStaticPaths. Префикс get заменен на более описательный generate, что лучше соответствует новой модели, где getStaticProps и getServerSideProps больше не нужны. Суффикс Paths заменен на Params, что более подходит для вложенной маршрутизации с несколькими динамическими сегментами.
Замена fallback
В директории pages свойство fallback, возвращаемое из getStaticPaths, используется для определения поведения страницы, которая не была предварительно отрендерена во время сборки. Это свойство может быть установлено в true для показа резервной страницы во время генерации, false для показа страницы 404 или blocking для генерации страницы во время запроса.
// Директория `pages`
export async function getStaticPaths() {
return {
paths: [],
fallback: 'blocking'
};
}
export async function getStaticProps({ params }) {
...
}
export default function Post({ post }) {
return ...
}В директории app свойство config.dynamicParams определяет, как обрабатываются параметры, не включенные в generateStaticParams:
true: (по умолчанию) Динамические сегменты, не включенные вgenerateStaticParams, генерируются по запросу.false: Динамические сегменты, не включенные вgenerateStaticParams, возвращают 404.
Это заменяет опцию fallback: true | false | 'blocking' из getStaticPaths в директории pages. Опция fallback: 'blocking' не включена в dynamicParams, так как разница между 'blocking' и true незначительна при использовании потоковой передачи.
// Директория `app`
export const dynamicParams = true;
export async function generateStaticParams() {
return [...]
}
async function getPost(params) {
...
}
export default async function Post({ params }) {
const post = await getPost(params);
return ...
}При установке dynamicParams в true (значение по умолчанию), если запрашивается сегмент маршрута, который не был сгенерирован, он будет отрендерен на сервере и закэширован.
Инкрементальная статическая регенерация (getStaticProps с revalidate)
В директории pages функция getStaticProps позволяет добавить поле revalidate для автоматической регенерации страницы через определенный промежуток времени.
// Директория `pages`
export async function getStaticProps() {
const res = await fetch(`https://.../posts`)
const posts = await res.json()
return {
props: { posts },
revalidate: 60,
}
}
export default function Index({ posts }) {
return (
<Layout>
<PostList posts={posts} />
</Layout>
)
}В директории app при запросе данных с помощью fetch() можно использовать revalidate, который будет кэшировать запрос на указанное количество секунд.
// Директория `app`
async function getPosts() {
const res = await fetch(`https://.../posts`, { next: { revalidate: 60 } })
const data = await res.json()
return data.posts
}
export default async function PostList() {
const posts = await getPosts()
return posts.map((post) => <div>{post.name}</div>)
}API-маршруты
API-маршруты продолжают работать в директории pages/api без изменений. Однако в директории app они заменены на Обработчики маршрутов (Route Handlers).
Обработчики маршрутов позволяют создавать пользовательские обработчики запросов для заданного маршрута с использованием Web API Request и Response.
export async function GET(request: Request) {}export async function GET(request) {}Полезно знать: Если вы ранее использовали API-маршруты для вызова внешнего API из клиента, теперь вы можете использовать Серверные компоненты (Server Components) для безопасного получения данных. Подробнее о запросе данных.
Шаг 7: Стилизация
В директории pages глобальные стили ограничены только файлом pages/_app.js. В директории app это ограничение снято. Глобальные стили можно добавлять в любой макет, страницу или компонент.
Tailwind CSS
Если вы используете Tailwind CSS, вам нужно добавить директорию app в файл tailwind.config.js:
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}', // <-- Добавьте эту строку
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
}Также необходимо импортировать глобальные стили в файле app/layout.js:
import '../styles/globals.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}Подробнее о стилизации с Tailwind CSS
Codemods
Next.js предоставляет преобразования Codemod для помощи в обновлении кодовой базы при устаревании функциональности. Подробнее см. Codemods.