Как генерировать динамические маршруты с помощью NextJS и Sanity.

Как и в прошлом уроке, в конце будет приведено эталонное изображение структуры моего файла и его содержимого, чтобы вы могли сравнить.

Добавление ссылок к каждому посту

Внутри вашего файла /pages/index.js нам нужно добавить ссылки на каждый пост, которые приведут нас на страницу этого конкретного поста.

Начните с добавления функции Link в NextJS.

import Link from "next/link";
Войдите в полноэкранный режим Выход из полноэкранного режима

Теперь внутри нашей переменной postsElements оберните весь элемент внутри тега Link.

const postsElements = posts.map((post, index) => (
    <Link key={index} href={`/posts/[slug]`} as={`/posts/${post.slug}`}>
      <div>
        <p>{new Date(post.publishedAt).toDateString().slice(4)}</p>
        <h3>{post.title}</h3>
      </div>
    </Link>
  ));
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь происходит несколько вещей.

  1. Мы переместили наш key={index} из div в Link, потому что теперь это самый внешний элемент.

  2. Существует атрибут href, с которым вы должны быть знакомы. Он указывает браузеру, куда переходить при нажатии на элемент. У меня он указывает на файл /posts/[slug], который мы скоро создадим.

  3. Есть также атрибут as. Это косметический атрибут, который превращает наш url в тот, который отображает значение slug, которое мы добавили к каждому посту. Это значение будет использоваться позже для поиска определенных постов.

Это весь код, необходимый для того, чтобы мы могли ссылаться на страницу поста при нажатии. Теперь пришло время добавить эту страницу в наш проект.


Отображение одного поста

Пришло время создать файл, о котором я только что говорил.
Добавьте папку posts в папку pages, затем добавьте файл [slug].js в эту новую папку. Теперь ваша папка pages должна выглядеть следующим образом.

Скобки вокруг имени файла добавлены для того, чтобы мы могли ссылаться на него позже.

Это называется динамическим маршрутом и будет упоминаться много раз в этом уроке, поэтому я советую вам ознакомиться с документацией по нему. Динамические маршруты NextJS

Внутри нашего файла /pages/posts/[slug].js/, который мы только что создали, мы будем добавлять наши компоненты по одному за раз.

Компонент поста

export default function Post() {
  return;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Стандартный компонент React. Мы добавим содержимое этого компонента через некоторое время.

getStaticProps

export async function getStaticProps({ params }) {
  const query = `*[_type == "post" && slug.current == $slug] {
    _id,
    title,
    publishedAt,
    'slug': slug.current,
    body
  }`;

  const options = { slug: params.slug };

  const post = await client.fetch(query, options);

  return {
    props: { post: post[0] },
  };
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Это очень похоже на getStaticProps, который мы добавили в наш файл /pages/index.js в прошлом уроке, но с несколькими дополнениями.

  • Мы добавляем наши { params } в качестве аргумента.

    • Чтобы немного объяснить это, getStaticProps имеет доступ к аргументу под названием context. Внутри этого аргумента context у вас есть несколько частей информации, из которых можно извлечь информацию. Мы используем destructuring, чтобы извлечь значение params.
    • Внутри этого объекта params находится url, связанный с этим файлом. Вот почему мы добавили скобки к [slug].js.
[_type == "post" && slug.current == $slug]
Вход в полноэкранный режим Выйти из полноэкранного режима
  • Тот же запрос, что и раньше, только в этот раз мы убираем опцию порядка, поскольку мы получаем только один пост, и вместо этого добавляем && slug.current == $slug.
    • Это позволит получить запись, которая имеет тип post И имеет slug.current (url, который мы указали в нашем посте), который совпадает с url текущей страницы (подробнее об этом чуть позже).
const options = { slug: params.slug }
Вход в полноэкранный режим Выход из полноэкранного режима
  • Мы присваиваем текущий url объекту.
    • Выше я упоминал, что мы используем params для получения текущего url и называем его slug. Вот где это вступает в игру.
    • Уникальный url для этого поста находится внутри params.slug. Мы присваиваем это значение ключу slug в объектной переменной options.
const post = await client.fetch(query, options)
Вход в полноэкранный режим Выход из полноэкранного режима
  • Здесь мы просто вызываем наш fetch, как и раньше, но теперь мы добавляем наш объект options, который мы создали.
    • Все это работает вместе, потому что наш объект options имеет наш url, хранящийся внутри него.
    • Наш запрос сопоставляет slug.current с этим url, который мы сохранили в опциях, чтобы получить сообщение для страницы, на которой мы находимся.
return {
    props: { post: post[0] },
  };
Вход в полноэкранный режим Выход из полноэкранного режима
  • Наконец, мы возвращаем наш post. Но так как мы получили массив, даже если мы получили только 1 пост, мы облегчаем себе задачу в нашем props, присваивая первый (и единственный) объект в нашем массиве post, чтобы мы могли импортировать его.

getStaticPaths

Безусловно, это самый сложный раздел для объяснения. Честно говоря, я не думаю, что смогу передать эту функцию в тексте, поэтому я собираюсь дать основные сведения и предоставить ссылки, которые объяснят происходящее более подробно.

Я упоминал о динамических маршрутах ранее, когда мы создавали [slug].js. В документации NextJS getStaticProps объясняется, что…

Если страница имеет динамические маршруты и использует getStaticProps, она должна определить список путей, которые будут генерироваться статически.

У нас есть динамические маршруты, и мы использовали getStaticProps, поэтому нам должно быть нужно это. Идея заключается в том, что NextJS теперь будет предварительно рендерить каждый путь, который вы укажете в getStaticPaths.

export async function getStaticPaths() {
  const query = `*[_type == "post"]{ 'slug': slug.current }`;

  const posts = await client.fetch(query);

  const paths =
    posts?.map((post) => ({
      params: {
        slug: post.slug,
      },
    })) || [];


  return {
    paths,
    fallback: false,
  };
}
Вход в полноэкранный режим Выход из полноэкранного режима

Чтобы увидеть примеры, подобные этому, вы можете просмотреть API-справку NextJS для getStaticPaths.

Вы можете заметить, что это выглядит очень похоже на наш getStaticProps. Нам нужно создать запрос, который будет искать каждый пост, но возвращать только значение slug.current (url, который мы ему дали), затем передать этот запрос нашему client.fetch.

Но что это за переменная paths? Она выглядит сложнее, чем есть на самом деле.

  • Сначала она проверяет, есть ли вообще посты, поэтому мы добавляем ? к постам. Если нет, то мы возвращаем пустой массив с || [].
    • По сути это означает posts.map, если посты есть, если нет, то возвращаем [].
  • Теперь внутри нашей map. Для каждого поста мы создаем…
    • { params: { slug: post.slug } }
    • Это получение url каждого поста и назначение его в качестве param для getStaticPaths для рендеринга во время сборки.
  • Наконец, мы возвращаем наши paths и fallback: false.
    • Значение fallback как false означает, что другие маршруты будут 404, если вы перейдете к тому, который мы не отобразили в нашей карте.

Рендеринг поста

Теперь мы собираемся использовать данные из поста, которые мы извлекли, внутри нашего компонента Post, который мы создали.

export default function Post({ post }) {
  return (
    <div>
      <h1>{post.title}</h1>
      <span>{new Date(post.publishedAt).toDateString()}</span>
    </div>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Код, аналогичный тому, что мы использовали на главной странице для отображения каждого поста, но на этот раз мы включаем день недели в дату.

Вы, наверное, заметили, что мы не возвращаем body нашего сообщения, это потому, что это требует немного больше усилий для рендеринга (и стилизации), поэтому я включу это в следующий небольшой урок.


Подведение итогов

Если вы перейдете на свою домашнюю страницу и щелкните на одном из своих постов, вы попадете на url этого поста, где будут отображены данные этого конкретного поста.

В следующем уроке я покажу вам, как…

  • Стиль веб-страницы с помощью компонентов Styled-Components
  • Стиль компонентов Rich Text

Мы также включим кнопку back для возврата на главную страницу из поста.


Ссылки

Структура файла

pages/posts/[slug].js

import client from "../../lib/sanity";

export default function Post({ post }) {
  return (
    <div>
      <h1>{post.title}</h1>
      <span>{new Date(post.publishedAt).toDateString()}</span>
    </div>
  );
}

export async function getStaticProps({ params }) {
  const query = `*[_type == "post" && slug.current == $slug] {
    _id,
    title,
    publishedAt,
    'slug': slug.current,
    body
  }`;

  const options = { slug: params.slug };

  const post = await client.fetch(query, options);

  return {
    props: { post: post[0] },
  };
}

export async function getStaticPaths() {
  const query = `*[_type == "post"]{ 'slug': slug.current }`;

  const posts = await client.fetch(query);

  const paths =
    posts?.map((post) => ({
      params: {
        slug: post.slug,
      },
    })) || [];

  return {
    paths,
    fallback: false,
  };
}
Вход в полноэкранный режим Выход из полноэкранного режима

Оцените статью
devanswers.ru
Добавить комментарий