Ссылки и навигация

Маршрутизатор 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
  • /aboutpages/about.js
  • /blog/hello-worldpages/blog/[slug].js

Любой <Link /> в области просмотра (изначально или при прокрутке) будет предварительно загружен по умолчанию (включая соответствующие данные) для страниц, использующих статическую генерацию (Static Generation). Данные для серверного рендеринга (server-rendered) загружаются только при клике на <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 может динамически переписывать маршруты, что нельзя проверить на стороне клиента без загрузки данных, которая пропускается при поверхностной маршрутизации, поэтому изменение поверхностного маршрута всегда должно рассматриваться как поверхностное.