Site icon 8HOST.COM

Постраничная верстка блога на Gatsby

Как мы знаем, посты не стоит загружать на одну и ту же страницу, лучше разбить их на отдельные страницы (хотя производительность Gatsby потянет их и на одной). В этом учебном материале мы рассмотрим плагин gatsby-awesome-pagination и с его помощью попробуем разбить наш архив записей на более удобные разделы.

Создание простого блога

Начнем работу с создания простого тестового блога. Используем для этого стартер gatsby-starter-blog, поскольку он поддерживает markdown.

$ gatsby new pagination-example https://github.com/gatsbyjs/gatsby-starter-blog
$ yarn add gatsby-awesome-pagination

Разбивка на страницы

Давайте импортируем метод paginate в файл gatsby-node.js и под запросом сообщений передадим ему объект. Ему нужно всего несколько вещей, а именно действие createPage, сам массив сообщений, доступное количество элементов на странице, заголовок для новой страницы архива и ее шаблон. Когда мы сделаем этот запрос, мы получим в свойстве pageContext кучу дополнительных вещей. Это свойство мы будем использовать для управления отображаемой страницей.

const { paginate } = require('gatsby-awesome-pagination');

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions

  const blogPost = path.resolve(`./src/templates/blog-post.js`)
  const result = await graphql(` ... `)

  paginate({
    createPage,
    items: result.data.allMarkdownRemark.edges,
    itemsPerPage: 3,
    pathPrefix: '/posts',
    component: path.resolve('src/templates/blog-archive.js')
  });
};

Шаблон страницы

Насколько нам известно, плагин gatsby-awesome-pagination предназначен только для постраничной верстки отдельных «архивных» страниц. Поэтому потому мы не будем добавлять нумерацию страниц на главную страницу стартера, а вместо этого сошлемся на отдельную страницу публикаций. Оформить группы публикаций на обычных страницах, вероятно, лучше всего с помощью статического запроса и чего-то вроде карусели для управления. Откройте файл index.js:

import React from "react";
import { Link, graphql } from "gatsby";
import Bio from "../components/bio";
import Layout from "../components/layout";

class BlogIndex extends React.Component {
  render() {
    const { data } = this.props;

    return (
      <Layout location={this.props.location}>
        <Bio />
        <Link to='/posts'><button>See All Posts</button></Link>
      </Layout>
    )
  }
};

export default BlogIndex;

Итак, в свойствах шаблона нашего архива, которые мы передадим в отдельный компонент Pager, уже есть доступ к pageContext запроса. Обратите внимание: вместо статического запроса нужно использовать обычный запрос, поскольку наш pageContext будет передавать значения в аргументы skip и limit, которые нам также нужно будет установить. Откройте blog-archive.js:

import Pager from '../components/pager';

export const pageQuery = graphql`
  query($skip: Int!, $limit: Int!) {
    site { ... }
    allMarkdownRemark(
        sort: { fields: [frontmatter___date], order: DESC},
        skip: $skip,
        limit: $limit
        ) {
      edges { ... }
    }
  }
`;

const BlogArchive = ({ data, pageContext, location }) => {
    const posts = data.allMarkdownRemark.edges;

    return (
      <Layout location={location}>
        {posts.map(({ node }) => {
            const title = node.frontmatter.title || node.fields.slug
            return (
              <article key={node.fields.slug}>
                <header>
                  <h3>
                    <Link to={node.fields.slug}> {title} </Link>
                  </h3>
                  <small>{node.frontmatter.date}</small>
                </header>
                <section>
                  <p dangerouslySetInnerHTML={{ __html: node.frontmatter.description || node.excerpt }} />
                </section>
              </article>
            )
        })}

        <Pager pageContext={pageContext} />
      </Layout>
    )
};

export default BlogArchive;

Компонент pager

Этот компонент хранится в файле components/pager.js. Последний наш шаг – получение путей к страницам из pageContext. В pageContext вы увидите свойства skip и limit, которые передаются запросу, а также pageNumber и numberOfPages, которые позволяют сгенерировать нужное количество страниц и ссылку для каждой страницы. Откройте components/pager.js:

import React from 'react';
import { Link } from 'gatsby';

const Pager = ({ pageContext }) => {
  console.log(pageContext);
  const { previousPagePath, nextPagePath } = pageContext;
  return (
    <nav style={{ display: 'flex', justifyContent: 'space-between' }}>
      <div>
        {previousPagePath && (
          <Link to={previousPagePath}>
            <button>← Newer Posts</button>
          </Link>
        )}
      </div>

      <div style={{ justifySelf: 'flex-end' }}>
        {nextPagePath && (
          <Link to={nextPagePath}>
            <button>Older Posts →</button>
          </Link>
        )}
      </div>
    </nav>
  );
};

export default Pager;

Публикации блога

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

В файле gatsby-node.js (где мы создаем страницы при помощи запроса) нам просто нужно добавить два поля, которые затем смогут получить доступ в pageContext. Если это первая публикация, то prev не должно существовать, иначе блог вернет последнюю запись. То же самое относится к свойству next (только оно вернет следующий пост, если таковой существует).

posts.forEach((post, index) => {
  createPage({
    path: post.node.fields.slug,
    component: blogPost,
    context: {
      slug: post.node.fields.slug,
      prev: index === 0 ? null : posts[index - 1].node,
      next: index === (posts.length - 1) ? null : posts[index + 1].node
    },
  })
});

То же самое относится к компоненту Pager: проверьте, существуют ли prev/next, вставьте их в Link, и все готово. Откройте blog-post.js:

const BlogPostTemplate = ({ data, pageContext, location }) => {
  const post = data.markdownRemark;
  const { prev, next } = pageContext;

  return (
    <Layout location={location}>
      <Link to='/posts'>Archive</Link>
      <article>
        <header>
          <h1> {post.frontmatter.title} </h1>
          <p> {post.frontmatter.date} </p>
        </header>
        <section dangerouslySetInnerHTML={{ __html: post.html }} />
        <hr />
      </article>

      <nav style={{ display: 'flex', justifyContent: 'space-between' }}>
        <div>
          {prev && <Link to={prev.fields.slug} rel="prev"> ← Last Post </Link>}
        </div>

        <div style={{ justifySelf: 'flex-end' }}>
          {next && <Link to={next.fields.slug} rel="next"> Next Post → </Link>}
        </div>
      </nav>
    </Layout>
  )
};

export default BlogPostTemplate;

Заключение

В этом руководстве мы показали, как работает плагин gatsby-awesome-pagination. Он все еще находится в активной разработке, хотя сложно представить, как можно улучшить и его без того невероятно простой дизайн.

Читайте также: Основы Gatsby CLI: шпаргалка по командам