Ссылки и навигация
Маршрутизатор Next.js позволяет выполнять клиентские переходы между страницами, аналогично одностраничному приложению.
Для этого предоставляется React-компонент под названием Link
.
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/">Главная</Link>
</li>
<li>
<Link href="/about">О нас</Link>
</li>
<li>
<Link href="/blog/hello-world">Пост в блоге</Link>
</li>
</ul>
)
}
export default Home
В приведённом выше примере используется несколько ссылок. Каждая из них сопоставляет путь (href
) с известной страницей:
/
→pages/index.js
/about
→pages/about.js
/blog/hello-world
→pages/blog/[slug].js
Любой <Link />
в области просмотра (изначально или при прокрутке) будет предварительно загружен по умолчанию (включая соответствующие данные) для страниц, использующих статическую генерацию. Данные для серверного рендеринга загружаются только при клике на <Link />
.
Ссылки на динамические пути
Вы также можете использовать интерполяцию для создания пути, что удобно для динамических сегментов маршрута. Например, для отображения списка постов, переданных компоненту как пропс:
import Link from 'next/link'
function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${encodeURIComponent(post.slug)}`}>
{post.title}
</Link>
</li>
))}
</ul>
)
}
export default Posts
В примере используется
encodeURIComponent
для обеспечения совместимости пути с utf-8.
Альтернативный вариант с использованием URL-объекта:
import Link from 'next/link'
function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link
href={{
pathname: '/blog/[slug]',
query: { slug: post.slug },
}}
>
{post.title}
</Link>
</li>
))}
</ul>
)
}
export default Posts
Теперь вместо интерполяции для создания пути мы используем URL-объект в href
, где:
pathname
— имя страницы в директорииpages
. В данном случае/blog/[slug]
.query
— объект с динамическим сегментом. В данном случаеslug
.
Получение доступа к роутеру
Примеры
Для доступа к объекту router
в React-компоненте вы можете использовать useRouter
или withRouter
.
В большинстве случаев рекомендуется использовать useRouter
.
Императивная маршрутизация
next/link
должен покрывать большинство ваших потребностей в маршрутизации, но вы также можете выполнять клиентскую навигацию без него — см. документацию по next/router
.
Следующий пример показывает, как выполнять базовые переходы между страницами с помощью useRouter
:
import { useRouter } from 'next/router'
export default function ReadMore() {
const router = useRouter()
return (
<button onClick={() => router.push('/about')}>
Нажмите, чтобы узнать больше
</button>
)
}
Поверхностная маршрутизация (Shallow Routing)
Примеры
Поверхностная маршрутизация позволяет изменять URL без повторного выполнения методов получения данных, включая getServerSideProps
, getStaticProps
и getInitialProps
.
Вы получите обновлённые pathname
и query
через объект router
(добавляемый useRouter
или withRouter
), без потери состояния.
Для включения поверхностной маршрутизации установите параметр shallow
в true
. Рассмотрим следующий пример:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Текущий URL — '/'
function Page() {
const router = useRouter()
useEffect(() => {
// Всегда выполняем навигацию после первого рендера
router.push('/?counter=10', undefined, { shallow: true })
}, [])
useEffect(() => {
// Счётчик изменился!
}, [router.query.counter])
}
export default Page
URL обновится до /?counter=10
, но страница не перезагрузится — изменится только состояние маршрута.
Вы также можете отслеживать изменения URL через componentDidUpdate
, как показано ниже:
componentDidUpdate(prevProps) {
const { pathname, query } = this.props.router
// проверяем, изменились ли пропсы, чтобы избежать бесконечного цикла
if (query.counter !== prevProps.router.query.counter) {
// загружаем данные на основе нового запроса
}
}
Ограничения
Поверхностная маршрутизация работает только для изменений URL на текущей странице. Например, предположим, что у нас есть другая страница pages/about.js
, и вы выполняете:
router.push('/?counter=10', '/about?counter=10', { shallow: true })
Поскольку это новая страница, текущая страница будет выгружена, загрузится новая, и будут ожидаться данные, несмотря на указание поверхностной маршрутизации.
При использовании поверхностной маршрутизации с middleware не гарантируется, что новая страница соответствует текущей, как это было ранее без middleware. Это связано с тем, что middleware может динамически переписывать маршруты, что нельзя проверить на стороне клиента без загрузки данных, которая пропускается при поверхностной маршрутизации. Поэтому изменения при поверхностной маршрутизации всегда должны рассматриваться как поверхностные.