Как создать блог GatsbyJS (часть 3): блог и страницы сообщений

Как создать блог GatsbyJS (часть 3): блог и страницы сообщений

28 февраля 2023 г.

Добро пожаловать в третью часть «Создания блога о Гэтсби»!

Мы добились значительного прогресса в предыдущих частях серии, создав основу нашего блога с компонентом «Макет» и несколькими ключевыми страницами. Теперь мы готовы погрузиться в самое сердце нашего блога: список блогов и отдельные страницы сообщений.

:::информация Некоторые части этой статьи, в том числе фрагменты кода, созданы искусственным интеллектом.

:::

В этой части серии мы будем использовать MDX для определения страниц нашего блога. MDX — это мощный инструмент, который позволяет нам создавать динамические интерактивные страницы с помощью Markdown и JSX. Мы также создадим список блогов, в котором будут отображаться все наши сообщения и отдельные страницы для каждого сообщения, где читатели смогут глубже погрузиться в содержание.

К концу этого поста у вас будет полностью функциональный блог с надежным дизайном, макетом и содержанием. Итак, давайте начнем с «Создание блога Гэтсби: блог и страницы сообщений» и выведем ваше блог-путешествие на новый уровень! И не забывайте следить за новостями этой серии, пока мы создаем и улучшаем наш блог.

Содержание

  • Использование многомерных выражений для сообщений в блогах
  • Страницы сообщений блога
  • Страница списка блогов
  • Заключение

Использование многомерных выражений для сообщений в блогах

MDX – это формат, сочетающий синтаксис Markdown – популярный способ написания простого текста, который можно легко преобразовать в HTML, – с JSX – синтаксисом, используемым в приложениях React для создания многократно используемых компонентов пользовательского интерфейса. MDX позволяет пользователям создавать динамические интерактивные страницы с помощью Markdown и React, позволяя разработчикам писать и отображать компоненты непосредственно в своем контенте.

Например, предположим, что вы хотите создать запись в блоге с фрагментом кода, который выделяет определенную функцию. С помощью MDX вы можете легко создать компонент React, который отображает фрагмент кода, а затем включает его в свой контент Markdown. Вот простой пример файла MDX, демонстрирующий эту концепцию:

import CodeSnippet from "./CodeSnippet";

# My Blog Post

Here is some text introducing my blog post.

<CodeSnippet code={`function add(a, b) {
  return a + b;
}`} />

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

Добавление MDX в Gatsby

Нам нужно настроить наш проект для использования MDX. Но сначала установите зависимости.

npm install @mdx-js/mdx @mdx-js/react gatsby-plugin-mdx 
  gatsby-remark-images gatsby-remark-copy-linked-files

<цитата>

ПРИМЕЧАНИЕ. Мы также будем использовать некоторые подключаемые модули Remark Gatsby. Remark — это мощный обработчик уценки для JavaScript, который позволяет легко анализировать, манипулировать и отображать содержимое уценки в ваших приложениях. Мы собираемся объединить Remark с многомерными выражениями, чтобы получить дополнительные функции, такие как изображения, а позже оглавление и другие полезные вещи.

Затем откройте gatsby-config.ts и добавьте следующую конфигурацию:

// gatsby-config.ts

const config: GatsbyConfig = {
  "plugins": {
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        name: 'images',
        path: './src/images/',
      },
      __key: 'images',
    },
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        name: 'pages',
        path: './src/pages/',
      },
      __key: 'pages',
    },
    // ↓ Add This
    // highlight-start
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        name: 'blog',
        path: './src/content',
      },
    },
    // highlight-end
    // ↓ REMOVE THIS
    'gatsby-plugin-mdx', // highlight-line
    // ↓ Add This
    // highlight-start
    {
      resolve: 'gatsby-plugin-mdx',
      options: {
        extensions: ['.md', '.mdx'],
        gatsbyRemarkPlugins: [
          'gatsby-remark-images',
          'gatsby-remark-copy-linked-files',
        ],
      },
    },
    // highlight-end
  }
}

<цитата>

ПРИМЕЧАНИЕ: **УБЕДИТЕСЬ, ЧТО ВЫ УДАЛИЛИ СУЩЕСТВУЮЩУЮ СТРОКУ ПЛАГИНА gatsby-plugin-mdx! Если вы этого не сделаете, вы получите странные ошибки

В приведенной выше конфигурации мы добавили новый маршрут файловой системы ./src/content для обработки Gatsby; здесь мы будем хранить записи нашего блога. Мы также настроили MDX и добавили несколько подключаемых модулей Remark для обработки изображений.

Мы создадим запись в блоге, создав новую папку и добавив файл index.mdx.

<цитата>

СОВЕТ: мы хотим создать папку для каждого сообщения, содержащую контент и другие ресурсы, такие как видео или изображения. С MDX также возможно, что мы можем захотеть создать некоторые пользовательские компоненты React внутри этой папки.

mkdir -p ./src/content/2023-02-18-01-00-00-my-first-blog-post
touch ./src/content/2023-02-18-01-00-00-my-first-blog-post/index.mdx

<цитата>

ПРИМЕЧАНИЕ. Вы можете изменить метку времени на текущий день, следуя инструкциям в этом руководстве.

Мы также добавим изображение в этот пост в блоге. Изображение может быть каким угодно. Если вы проверите ветку GitHub для этой статьи, вы найдете изображение, которое я использовал, и вы, безусловно, можете скопировать это изображение и использовать его. В противном случае добавьте собственное изображение.

mv ~/path/to/profile.jpg ./src/content/2023-02-18-01-00-00-my-first-blog-post/profile.jpg

Добавьте эту уценку в файл index.mdx:

---
date: 2023-02-18 01:00:00
slug: my-first-blog-post
title: My First Blog Post
author: John Smith
---

## Introduction

Welcome to my first-ever blog post! I'm excited to finally take the plunge and
start sharing my thoughts, experiences, and ideas with the world. I've been
following blogs for years and have always been inspired by the amazing content
and community that exists in the blogosphere. Now, it's my turn to join the
conversation and contribute my own voice to the mix.

In this post, I'll be introducing myself, sharing my motivation for starting a
blog, and giving you a preview of the types of content you can expect to see
from me in the future. Whether you're a seasoned blogger or a first-time reader,
I hope you'll find this post to be a fun and engaging introduction to who I am
and what I have to offer. So, without further ado, let's get started!

## About Me

![My profile photo](./profile.jpg)

Hey there, I'm John Smith, a web developer and Javascript enthusiast from
Seattle, Washington. I've been passionate about web development since I built my
first website as a teenager, and I've been hooked ever since. Over the years,
I've gained experience in a wide range of web technologies, including HTML, CSS,
and JavaScript, and I'm always looking for new and innovative ways to create
amazing web experiences.

### Why I started this blog

I started this blog as a way to share my knowledge and experiences with other
developers, enthusiasts, and beginners alike. I believe that the best way to
learn is by doing, and my hope is to inspire others to dive in and start
building amazing things on the web.

### What I like doing in my spare time

When I'm not coding or writing about web development, you can find me hiking in
the Pacific Northwest or cheering on the Seahawks. Thanks for checking out my
blog, and I can't wait to share more with you!

<цитата>

ПРИМЕЧАНИЕ. То, что находится в верхней части страницы, обернутое метками ---, называется "основной вопрос". Это полезные метаданные о сообщении в блоге, и вы можете добавить туда любую информацию, которая станет доступной внутри компонента React для вашего сообщения в блоге. Так что мы будем часто использовать передний план.

Страницы сообщений блога

Далее мы создадим страницы сообщений блога.

Создайте файл шаблона BlogPostTemplate.tsx.

Файл BlogPostTemplate.tsx представляет собой файл шаблона. Мы собираемся использовать его ниже в gatsby-node.ts для программного создания наших сообщений в блоге из запроса GraphQL. Шаблону будет передан идентификатор и данные записи в блоге.

mkdir ./src/templates
touch ./src/templates/BlogPostTemplate.tsx

// ./src/templates/BlogPostTemplate.tsx
import { MDXProvider } from '@mdx-js/react';
import { graphql, PageProps } from 'gatsby';
import React from 'react';
import { PageLayout } from '../components/page-layout';

const BlogPostTemplate: React.FC<PageProps<Queries.BlogPostQuery>> = ({
  data,
  children, // ← This is the post content
}) => {
  return (
    <PageLayout>
      <h1 className="mb-8 text-4xl font-bold sm:text-5xl">
        {data.mdx?.frontmatter?.title}
      </h1>
      <div className="mb-8">
        <span className="text-sm font-thin">
          By {data.mdx?.frontmatter?.author} on {data.mdx?.frontmatter?.date}
        </span>
      </div>
      <MDXProvider>{children}</MDXProvider>
    </PageLayout>
  );
};

export default BlogPostTemplate;

export const query = graphql`
  query BlogPost($id: String!) {
    mdx(id: { eq: $id }) {
      frontmatter {
        title
        author
        date(formatString: "MMMM DD, YYYY")
      }
    }
  }
`;

Создайте gatsby-node.ts

Мы будем использовать API, доступный нам в gatsby-node.ts. Этот файл дает нам программный доступ к созданию страницы для нашего блога. Мы собираемся использовать его возможности для создания наших сообщений в блоге и нашей страницы со списком блогов.

touch gatsby-node.ts

// gatsby-node.ts
import { CreatePagesArgs } from 'gatsby';
import path from 'path';

exports.createPages = async ({
  graphql,
  actions,
  reporter,
}: CreatePagesArgs) => {
  const { createPage } = actions;

  const BlogPostTemplate = path.resolve('./src/templates/BlogPostTemplate.tsx');

  const result = await graphql<Queries.GatsbyNodeCreatePagesQuery>(
    `
      query GatsbyNodeCreatePages {
        allMdx {
          nodes {
            id
            frontmatter {
              slug
            }
            internal {
              contentFilePath
            }
          }
        }
      }
    `
  );

  if (result.errors) {
    reporter.panicOnBuild(
      'There was an error loading the MDX result',
      result.errors
    );
  }

  result.data?.allMdx.nodes.forEach((node) => {
    createPage({
      path: `/blog/${node.frontmatter?.slug}`,
      component: `${BlogPostTemplate}?__contentFilePath=${node.internal.contentFilePath}`,
      context: { id: node.id },
    });
  });
};

<цитата>

ПРИМЕЧАНИЕ. Атрибут component внизу может выглядеть странно. ознакомьтесь с документацией для объяснение, почему это выглядит именно так.

Стили сообщений в блоге

Давайте вернемся назад и посмотрим, как сейчас выглядит наш сайт.

Unstyled blog post

Ну и весь контент есть. Но он еще не стилизован и выглядит не очень приятно. Давайте это исправим.

В системе MDX, @mdx-js/react, есть поставщик, который мы используем в компоненте BlogPostTemplate, который называется MdxProvider. Мы создадим несколько стилей и передадим их провайдеру, чтобы он знал, как правильно стилизовать наш контент.

<цитата>

ПРИМЕЧАНИЕ. Если вы хотите узнать больше о параметрах стиля, доступных для компонента поставщика MdxProvider, вот ссылка на документацию.

Мы определим наши стили в новом модуле, расположенном в нашем каталоге components:

mkdir -p ./components/mdx-components/components
touch ./components/mdx-components/index.ts
touch ./components/mdx-components/styles.tsx
touch ./components/mdx-components/components/MainContent.tsx

<цитата>

ПРИМЕЧАНИЕ. Мы создаем папку для хранения наших стилей и компонентов MDX, поскольку в последующих частях этой серии мы определим пользовательские компоненты для использования с MDX и будем хранить их все в одном месте. является хорошей организационной практикой.

// ./src/components/mdx-components/index.ts
export * from './styles';
export * from './components/MainContent';

// ./src/components/mdx-components/styles.tsx
import styled from '@emotion/styled';
import tw from 'twin.macro';

const H1 = styled.h1`
  ${tw`mb-8 text-3xl font-medium uppercase`};
`;

const H2 = styled.h2`
  ${tw`mb-8 text-2xl font-medium`}
`;

const H3 = styled.h3`
  ${tw`mb-8 text-xl font-medium`};
`;

const H4 = styled.h4`
  ${tw`mb-4 uppercase text-sm font-bold`}
`;

const H5 = styled.h5`
  ${tw`font-bold text-sm`}
`;

const Paragraph = styled.p`
  ${tw`mb-8 text-lg leading-relaxed`};
`;

const Blockquote = styled.blockquote`
  ${tw`pl-4 text-gray-700 border-l-2 border-blue-200`}
`;

const UnorderedList = styled.ul`
  ${tw`pl-5 mb-8 list-disc`}

  > li {
    ${tw`mb-4`}
  }
`;

const OrderedList = styled.ol`
  ${tw`pl-5 mb-8 list-decimal`}

  > li {
    ${tw`mb-4`}
  }
`;

const ListElement = styled.li`
  ${tw`text-lg`}
  > p {
    ${tw`mb-0`};
  }
  > blockquote {
    ${tw`mt-4`}
  }
`;

const Anchor = styled.a`
  ${tw`underline text-blue-500`};
`;

export const components = {
  h1: H1,
  h2: H2,
  h3: H3,
  h4: H4,
  h5: H5,
  p: Paragraph,
  ol: OrderedList,
  ul: UnorderedList,
  li: ListElement,
  a: Anchor,
  blockquote: Blockquote,
};

<цитата>

ПРИМЕЧАНИЕ. В приведенных выше стилях мы используем утилиту styled из @emotion/styled и twin.macro< /код> утилита. Утилита twin.macro позволяет нам определять классы Tailwind в стилях, основанных на эмоциях.

// ./src/components/mdx-components/components/MainContent.tsx
import styled from '@emotion/styled';
import tw from 'twin.macro';

export const MainContent = styled.article`
  ${tw`max-w-4xl px-4 py-12 mx-auto my-12 text-black lg:border lg:border-gray-200 lg:p-12`};

  &&& {
    .gatsby-resp-image-wrapper {
      ${tw`w-full mb-8`};
      max-width: 100% !important;
      margin-left: 0 !important;
      margin-right: 0 !important;

      img {
        object-fit: cover;
      }
    }
  }
`;

<цитата>

ПРИМЕЧАНИЕ. Компонент MainContent будет обертывать наш контент некоторыми "глобальными" стилями. Мы используем утилиту styled от @emotion/styled, чтобы добавить произвольный CSS для наших изображений в стиле гэтсби и убедиться, что они хорошо смотрятся в нашем блоге. Не стесняйтесь настраивать приведенный выше CSS по своему вкусу!

Теперь мы можем добавить эти стили в MdxProvider нашего компонента BlogPostTemplate:

// ./src/templates/BlogPostTemplate.tsx
import { MDXProvider } from '@mdx-js/react';
import { graphql, PageProps } from 'gatsby';
import React from 'react';
// ↓ Add This
import { components, MainContent } from '../components/mdx-components'; // highlight-line
import { PageLayout } from '../components/page-layout';

const BlogPostTemplate: React.FC<PageProps<Queries.BlogPostQuery>> = ({
  data,
  children,
}) => {
  return (
    <PageLayout>
      // ↓ Add This
      <MainContent>
        {' '}
        // highlight-line
        <h1 className="mb-8 text-4xl font-bold sm:text-5xl">
          {data.mdx?.frontmatter?.title}
        </h1>
        <div className="mb-8">
          <span className="text-sm font-thin">
            By {data.mdx?.frontmatter?.author} on {data.mdx?.frontmatter?.date}
          </span>
        </div>
        // ↓ Update This
        <MDXProvider components={components}>{children}</MDXProvider> // highlight-line
      </MainContent>
    </PageLayout>
  );
};

export default BlogPostTemplate;

export const query = graphql`
  query BlogPost($id: String!) {
    mdx(id: { eq: $id }) {
      frontmatter {
        title
        author
        date(formatString: "MMMM DD, YYYY")
      }
    }
  }
`;

Давайте посмотрим, как это выглядит сейчас:

Styled blog post

Гораздо лучше!

Страница со списком блогов

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

Давайте углубимся в это!

Вот как настроить страницу со списком блогов:

touch ./src/pages/blog.tsx

import { graphql, Link, PageProps } from 'gatsby';
import React from 'react';
import { PageLayout } from '../components/page-layout';

const BlogPage: React.FC<PageProps<Queries.BlogPageQuery>> = ({ data }) => {
  return (
    <PageLayout>
      <h1 className="mb-8 text-center text-4xl font-bold sm:text-5xl">Blog</h1>
      <ul className="mx-auto max-w-3xl p-4 sm:p-0">
        {data.allMdx.edges.map(({ node }) => (
          <li key={node.id} className="mb-4 last-of-type:mb-0">
            <Link
              to={`/blog/${node.frontmatter?.slug}`}
              className="block rounded-lg border border-gray-400 p-6"
            >
              <h2 className="mb-4 text-xl font-bold">
                {node.frontmatter?.title}
              </h2>
              <span className="mb-2 block text-sm font-thin">
                By {node.frontmatter?.author} on {node.frontmatter?.date}
              </span>
              <span className="block text-lg">{node.excerpt}</span>
            </Link>
          </li>
        ))}
      </ul>
    </PageLayout>
  );
};

export default BlogPage;

export const query = graphql`
  query BlogPage {
    allMdx(sort: { frontmatter: { date: DESC } }) {
      edges {
        node {
          id
          excerpt(pruneLength: 160)
          frontmatter {
            title
            author
            date(formatString: "MMMM DD, YYYY")
            slug
          }
        }
      }
    }
  }
`;

А вот как выглядит страница localhost:8000/blog/:

Blog list page

<цитата>

ПРИМЕЧАНИЕ. Я добавил еще несколько сообщений в блог, чтобы он выглядел более «живым». Спасибо, chatGPT!

Заключение

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

Когда мы создадим наш блог, следующим шагом будет добавление избранного изображения к каждому сообщению в блоге. Это поможет придать нашему блогу более профессиональный вид и сделать наш контент более привлекательным для читателей. Так что следите за обновлениями в следующем выпуске «Создаем блог о Гэтсби», и давайте вместе поднимем наш блог на новый уровень!

И вот исходный код для этого поста: MachineServant GitHub: создайте блог о Гэтсби (часть 3)


Также опубликовано здесь.


Оригинал