Страницы и макеты
Pages Router использует файловую систему для маршрутизации на основе концепции страниц.
Когда файл добавляется в директорию pages
, он автоматически становится доступным как маршрут.
В Next.js страница — это React-компонент, экспортируемый из файла .js
, .jsx
, .ts
или .tsx
в директории pages
. Каждая страница связана с маршрутом на основе имени файла.
Пример: Если вы создадите pages/about.js
, который экспортирует React-компонент, как показано ниже, он будет доступен по адресу /about
.
export default function About() {
return <div>About</div>
}
Индексные маршруты
Роутер автоматически направляет файлы с именем index
в корень директории.
pages/index.js
→/
pages/blog/index.js
→/blog
Вложенные маршруты
Роутер поддерживает вложенные файлы. Если вы создадите вложенную структуру папок, файлы будут автоматически маршрутизироваться аналогичным образом.
pages/blog/first-post.js
→/blog/first-post
pages/dashboard/settings/username.js
→/dashboard/settings/username
Страницы с динамическими маршрутами
Next.js поддерживает страницы с динамическими маршрутами. Например, если вы создадите файл pages/posts/[id].js
, он будет доступен по адресам posts/1
, posts/2
и т.д.
Чтобы узнать больше о динамической маршрутизации, ознакомьтесь с документацией по динамическим маршрутам.
Шаблон макета
Модель React позволяет разбить страницу на серию компонентов. Многие из этих компонентов часто повторно используются между страницами. Например, у вас может быть одинаковая навигационная панель и подвал на каждой странице.
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
Примеры
Общий макет с Custom App
Если у вас только один макет для всего приложения, вы можете создать Custom App и обернуть приложение этим макетом. Поскольку компонент <Layout />
повторно используется при смене страниц, его состояние (например, значения полей ввода) сохраняется.
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
Индивидуальные макеты для страниц
Если вам нужно несколько макетов, вы можете добавить свойство getLayout
к вашей странице, позволяя возвращать React-компонент для макета. Это позволяет определять макет для каждой страницы индивидуально. Поскольку мы возвращаем функцию, при необходимости можно создавать сложные вложенные макеты.
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
export default function Page() {
return (
/** Ваш контент */
)
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
export default function MyApp({ Component, pageProps }) {
// Используем макет, определенный на уровне страницы, если он доступен
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
При переходе между страницами мы хотим сохранять состояние страницы (значения полей ввода, позицию прокрутки и т.д.) для работы в стиле Single-Page Application (SPA).
Этот шаблон макета обеспечивает сохранение состояния, потому что дерево React-компонентов сохраняется между переходами. React может понимать, какие элементы изменились, чтобы сохранить состояние.
Полезно знать: Этот процесс называется согласованием (reconciliation), который позволяет React определять, какие элементы изменились.
С TypeScript
При использовании TypeScript сначала нужно создать новый тип для ваших страниц, включающий функцию getLayout
. Затем необходимо создать новый тип для AppProps
, который переопределяет свойство Component
, чтобы использовать ранее созданный тип.
import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
import type { NextPageWithLayout } from './_app'
const Page: NextPageWithLayout = () => {
return <p>hello world</p>
}
Page.getLayout = function getLayout(page: ReactElement) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
export default Page
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
const Page = () => {
return <p>hello world</p>
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
export default Page
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode
}
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// Используем макет, определенный на уровне страницы, если он доступен
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
export default function MyApp({ Component, pageProps }) {
// Используем макет, определенный на уровне страницы, если он доступен
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
Получение данных
Внутри вашего макета вы можете получать данные на стороне клиента с помощью useEffect
или библиотеки типа SWR. Поскольку этот файл не является страницей, вы не можете использовать getStaticProps
или getServerSideProps
.
import useSWR from 'swr'
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
const { data, error } = useSWR('/api/navigation', fetcher)
if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>
return (
<>
<Navbar links={data.links} />
<main>{children}</main>
<Footer />
</>
)
}