Использование Gatsby для создания блога с разбивкой на страницы для сообщений блога
Если вы хотите создать статический веб-сайт или блог и разбираетесь в веб-разработке, у вас есть множество вариантов генераторов статических веб-сайтов.
Если вы хотите использовать React для создания своего статического веб-сайта, Gatsby может быть выбором, который соответствует вашим потребностям. Согласно веб-сайту Gatsby, он используется многими крупными корпорациями для создания своих веб-сайтов.
В этой части мы будем использовать Gatsby для создания небольшого блога с разбивкой на страницы для сообщений блога и меню, которое позволяет читателям получать категории. После того, как читатель щелкнет ссылку категории, он перейдет на страницу со списком всех сообщений, помеченных этими категориями.
Гэтсби может получать контент из файлов и из сетевых источников, таких как конечная точка API. Контент получается через конечные точки GraphQL, в которых вы можете отправлять сообщения для отображения данных так, как вы хотите, с помощью шаблонов.
Вы также можете создавать файлы статических страниц, которые отображают неизменяемое содержимое. Вы пишете код, чтобы получать контент и отображать его с помощью шаблонов.
Затем вы создаете статические веб-страницы из написанного кода. Плагины для Gatsby предоставят вам желаемую функциональность.
Чтобы создать статический веб-сайт или блог, нужно пройти несколько шагов. Кроме того, вы не можете использовать большинство сторонних компонентов React с Gatsby, поскольку они динамические.
Кроме того, служебная программа командной строки имеет проблемы с путями с учетом регистра в Windows, что приводит к RelayParser: Encountered duplicate definitions for one or more documents
из-за дублирования путей к файлам без учета регистра в различных частях папки проекта.
Следовательно, Linux или Mac OS, вероятно, являются лучшим выбором для использования Gatsby CLI. Кроме того, в одном файле должны выполняться запросы нескольких страниц, и все они передаются шаблонам в одном файле.
gatsby-node.js
в проекте - это место, где выполняется запрос содержимого.
Теперь, когда мы знаем о предостережениях, мы можем приступить к созданию нашего веб-сайта. Мы создадим блог с некоторыми записями в блоге. Макет блога - это верхний колонтитул и содержимое остальной части страницы.
У нас будет шаблон для отображения всех записей блога с разбивкой на страницы по пять сообщений в каждой. Также у нас будет страница категорий для отображения списка статей с заданным тегом. Для стилизации будем использовать Bootstrap.
Начнем с установки Gatsby CLI, запустив npm install -g gatsby-cli
. После того, как он установлен, мы запускаем программу CLI, чтобы добавить начальный код для веб-сайта. Мы запускаем gatsby new gatsby-blog
, чтобы создать новый сайт.
Затем мы устанавливаем несколько дополнительных пакетов для Гэтсби, чтобы делать такие вещи, как получение контента из файлов разметки и отображение изображений.
Установите пакеты, запустив:
npm i @mdx-js/mdx @mdx-js/react bootstrap gatsby-image gatsby-paginate gatsby-plugin-manifest gatsby-plugin-mdx gatsby-plugin-offline gatsby-plugin-react-helmet gatsby-plugin-sharp gatsby-remark-images gatsby-source-filesystem gatsby-transformer-remark gatsby-transformer-sharp jquery lodash popper.js react-helmet
Основными пакетами в этом списке являются gatsby-image
для отображения изображений, gatsby-source-filesystem
для получения файлов разметки и отображения их в наших шаблонах.
react-helmet
за добавление тега head
в наш блог для включения внешних файлов CSS и скриптов. gatsby-transformer-remark
предназначен для преобразования разметки в текст HTML. gatsby-transformer-sharp
предназначен для преобразования изображений из различных источников для отображения на нашем веб-сайте.
Завершив установку пакета, мы можем приступить к написанию кода. В папке components
сначала работаем над header.js
.
Там мы помещаем:
import { Link } from "gatsby" import PropTypes from "prop-types" import React from "react" import { StaticQuery, graphql } from "gatsby" import _ from "lodash" import "./header.css" const Header = ({ siteTitle }) => ( <nav className="navbar navbar-expand-md bg-dark navbar-dark"> <a className="navbar-brand" href="#"> {siteTitle} </a> <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar" > <span className="navbar-toggler-icon"></span> </button> <div className="collapse navbar-collapse" id="collapsibleNavbar"> <ul className="navbar-nav"> <li className="nav-item"> <Link to="blog/page/1" className="nav-link"> Home </Link> </li> <li className="nav-item dropdown"> <a className="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" > Categories </a> <div className="dropdown-menu" aria-labelledby="navbarDropdown"> <StaticQuery query={graphql` query CategoryQuery { allMarkdownRemark(limit: 2000) { group(field: frontmatter___tags) { fieldValue } } } `} render={data => { return data.allMarkdownRemark.group.map(g => { return ( <Link to={`tags/${g.fieldValue}`} className="dropdown-item" > {_.capitalize(g.fieldValue)} </Link> ) }) }} /> </div> </li> </ul> </div> </nav> ) Header.propTypes = { siteTitle: PropTypes.string, } Header.defaultProps = { siteTitle: ``, } export default Header
Это добавит в наше приложение панель навигации Bootstrap. Мы получаем теги для всех наших сообщений и отображаем их в раскрывающемся списке на панели навигации.
В header.css
мы помещаем:
.navbar-brand { font-size: 20px; margin-top: -20px; } nav.navbar { padding-bottom: 0px; height: 60px; }
Чтобы изменить высоту панели навигации и изменить размер шрифта и поля.
Далее работаем над общей версткой блога. Мы создаем файл с именем layout.js
в папке components
, если он еще не существует, и добавляем:
/** * Layout component that queries for data * with Gatsby's useStaticQuery component * * See: https://www.gatsbyjs.org/docs/use-static-query/ */ import React from "react" import PropTypes from "prop-types" import { useStaticQuery, graphql } from "gatsby" import { Helmet } from "react-helmet" import Header from "./header" import "./layout.css" const Layout = ({ children }) => { const data = useStaticQuery(graphql` query SiteTitleQuery { site { siteMetadata { title } } } `) return ( <> <Helmet> <title>Gatsby Blog</title> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossOrigin="anonymous" ></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossOrigin="anonymous" ></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossOrigin="anonymous" ></script> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossOrigin="anonymous" /> </Helmet> <Header siteTitle={data.site.siteMetadata.title} /> <div style={{ margin: `0 auto`, maxWidth: 960, padding: `0px 1.0875rem 1.45rem`, paddingTop: 0, }} > <main>{children}</main> <footer> © {new Date().getFullYear()}, Built with {` `} <a href="https://www.gatsbyjs.org">Gatsby</a> </footer> </div> </> ) } Layout.propTypes = { children: PropTypes.node.isRequired, } export default Layout
В этом файле мы добавляем тег head
для наших HTML-страниц. Мы включаем Bootstrap CSS и файлы кода и jQuery в компонент Helmet
, который будет преобразован в тег head
на наших страницах.
Далее мы добавляем наш контент, создаем папку markdown-pages
в папке src
. Затем мы создаем несколько файлов уценки.
Добавляем в папку markdown-pages
следующие файлы:
check-variable-number.md clone-array.md concat-array.md exponentiation.md get-length-obj.md repeat-string.md send-email.md
Затем в check-variable-number.md
мы добавляем:
--- path: "/blog/check-variable-number" date: "2019-05-04" title: "How to Check if a Variable is a Number" tags: ["number"] --- We can check if a variable is a number in multiple ways. ## isNaN We can check by calling `isNaN` with the variable as the argument. It also detects if a string’s content is a number. For example: ``` isNaN(1) // false isNaN('1') // false isNaN('abc') // true ``` **Note:** `isNaN(null)` is `true` . ### typeof Operator We can use the `typeof` operator before a variable to check if it’s a number, like so: ``` typeof 1 == 'number' // true typeof '1' == 'number' // false ``` ![](https://cdn-images-1.medium.com/max/800/1*3X6EiKc-njoRpCB1AWnv3Q.png)
В clone-array.md
мы добавляем:
--- path: "/blog/clone-array" date: "2019-05-04" title: "How to Clone Array in JavaScript" tags: ["array"] --- There are a few ways to clone an array in JavaScript, ### Object.assign `Object.assign` allows us to make a shallow copy of any kind of object including arrays. For example: <pre>const a = [1,2,3]; const b = Object.assign([], a); // [1,2,3]</pre> ### Array.slice The `Array.slice` function returns a copy of the original array. For example: <pre>const a = [1,2,3]; const b = a.slice(0); // [1,2,3]</pre> ### Array.from The `Array.slice` function returns a copy of the original array. It takes array like objects like `Set` and it also takes an array as an argument. <pre>const a = [1,2,3]; const b = Array.from(a); // [1,2,3]</pre> ### Spread Operator The fastest way to copy an array, which is available with ES6 or later, is the spread operator. <pre>const a = [1,2,3]; const b = [...a]; // [1,2,3]</pre> ### JSON.parse and JSON.stringify This allows for deep copy of an array and only works if the objects in the array are plain objects. It can be used like this: <pre>const a = [1,2,3]; const b = JSON.parse(JSON.stringify(a)); // [1,2,3]</pre>
Далее в concat-array.md
мы добавляем:
--- path: "/blog/concat-array" date: "2019-05-04" title: "How to Concatenate Array in JavaScript" tags: ["array"] --- There are a few ways to concatenate arrays in JavaScript. ## Array.concat We can all `Array.concat` on an array to combine 2 arrays and return the new one. For example: ``` const a = [1,2,3]; const b = [4,5]; const c = a.concat(b) // [1,2,3,4,5] ``` ## Array.push We can push elements of one array into another. ``` const a = [1,2,3]; const b = [4,5]; let c = Object.assign([], a); for (let i = 0; i < b.length; i++){ c.push(b[i]); } console.log(c); // [1,2,3,4,5] ``` What we did is make a copy of `a` and assigned it to `c` , then pushed the elements of `b` by looping through it and adding them to the end of `c` . ## Spread Operator With ES6 or later, we can use the spread operator to spread the items from another array into a new array by doing the following: ``` const a = [1,2,3]; const b = [4,5]; const c = [...a, ...b]; console.log(c); // [1,2,3,4,5] ``` ![](https://cdn-images-1.medium.com/max/800/1*3X6EiKc-njoRpCB1AWnv3Q.png)
В exponentiation.md
мы добавляем:
--- path: "/blog/exponentiation" date: "2019-05-04" title: "How to do Exponentiation in JavaScript" tags: ["number"] --- There are multiple to compute exponents with JavaScript. The newest way is the exponentiation operator `**`, available with ES2016 or higher. For example, we can do this: ``` const a = 2 ** 3; // 8 ``` It is right associative, so `a ** b ** c` is equal to `a ** (b ** c)`. This works with all exponents. For example: ``` const a = 2 ** (3 ** 4); const b = 2 ** 3 ** 4; a == b // true, both are 2.4178516392292583e+24 ``` Detail browser compatibility is available at [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Browser_compatibility](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Browser_compatibility) We can also use the `Math.pow` function, like this: ``` const a = Math.pow(2,3) // 8 ``` It takes 2 arguments, the first is the base and the second is the exponent. `Math.pow` works with all exponents. `Math.pow` is compatible with all recent browsers. ![](https://cdn-images-1.medium.com/max/800/1*3X6EiKc-njoRpCB1AWnv3Q.png)
В get-length-obj.md
мы добавляем:
--- path: "/blog/get-length-obj" date: "2019-05-04" title: "How to Get the Length of An Object" tags: ["object"] --- There are 2 ways to get the length of the list of keys of an object. ## Object.keys `Object.keys` gets the top level list of keys of an object and returns an array of them. For example: ``` const a = {foo: 1, bar: 2}; const length = Object.keys(a).length // 2 ``` ## Object.getPropertyNames `Object.getPropertyNames` also gets a list of all top level of keys of an object and return them as an array. For example: ``` const a = {foo: 1, bar: 2}; const length = Object.`getOwnPropertyNames`(a).length // 2 ``` ## for…in Loop There is a special loop for looping through the keys of an object. You can do the following: ``` const a = {foo: 1, bar: 2}; let keysCount = 0; for (let key in a) { keysCount++; } console.log(`keysCount) // 2 ``` ![](https://cdn-images-1.medium.com/max/800/1*3X6EiKc-njoRpCB1AWnv3Q.png)
В repeat-strings.md
мы добавляем:
--- path: "/blog/repeat-strings" date: "2019-05-04" title: "How to Repeat Strings with JavaScript" tags: ["string"] --- There are a few ways to repeat a string in JavaScript. JavaScript strings have a built in `repeat()` function. You can also use a loop to do the same thing. ## String.repeat Function To use the `repeat` function, you pass in the number of times you want to repeat the string as an argument. It returns a new string For example: ``` const hello = "hello"; const hello5 = A.repeat(5); console.log(hello5); // "hellohellohellohellohello" ``` ## Use a loop You can use `for` loop and `while` loop to do repeatedly concatenate strings. Using a `for` loop, you can do: ``` const hello = "hello"; ``` With a `while` loop, you can do: ``` const hello = "hello"; ``` They both involve increment indexes up to the maximum. ![](https://cdn-images-1.medium.com/max/800/1*3X6EiKc-njoRpCB1AWnv3Q.png)
В send-email.md
мы добавляем:
--- path: "/blog/send-email" date: "2019-05-04" title: "How to Send Email with SendGrid in Node.js Apps" tags: ["array"] --- SendGrid is a great service made by Twilio for sending emails. Rather than setting up your own email server for sending email with your apps, we use SendGrid to do the hard work for us. It also decrease the chance of email ending up in spam since it is a known trustworthy service. It also has very easy to use libraries for various platforms for sending emails. Node.js is one of the platforms that are supported. To send emails with SendGrid, install the SendGrid SDK package by running `npm i @sendgrid/mail` . Then in your code, add `const sgMail = require(‘@sendgrid/mail’);` to import the installed package. Then in your code, you send email by: ``` sgMail.setApiKey(process.env.SENDGRID_API_KEY); const msg = { to: email, from: '[email protected]', subject: 'Example Email', text: ` Dear user, Here is your email.`, html: ` <p>Dear user,</p></pre> Here is your email.</p>`, }; sgMail.send(msg); ``` where `process.env.SENDGRID_API_KEY` is the SendGrid’s API, which should be stored as an environment variable since it is a secret. Testing is easy since you don’t need to set up a local development email server. Sending email is this simple and easy with SendGrid API. It is also free if you send small amounts of email, which is a great benefit. ![](https://cdn-images-1.medium.com/max/800/1*EdbfsnL3ABxWj2iVWmoIWA.png)
В каждом файле у нас есть метаданные между пунктирными линиями. Они будут получены из запросов GraphQL и могут отображаться на наших страницах, а мы будем их отображать.
Теперь, когда мы поработали над нашим контентом, мы можем работать со статическими страницами. Мы меняем страницы 404 по умолчанию, добавляя верхнюю панель навигации.
В 404.js
мы заменяем существующий код на:
import React from "react" import Layout from "../components/layout" import SEO from "../components/seo" import Header from "../components/header"; const NotFoundPage = () => ( <Layout> <SEO title="404: Not found" /> <h1>NOT FOUND</h1> <p>You just hit a route that doesn't exist... the sadness.</p> </Layout> ) export default NotFoundPage
Все, что есть в компоненте Layout
, будет применено к этой странице, включая панель навигации и стили Bootstrap.
В index.js
мы заменяем существующий код на:
import React from "react" import Layout from "../components/layout" import SEO from "../components/seo" import { useEffect } from "react" import { navigate } from "gatsby" const IndexPage = () => { useEffect(() => { navigate("/blog/page/1") }, []) return ( <Layout> <SEO title="Home" /> </Layout> ) } export default IndexPage
Для перенаправления на наш шаблон блога, который мы создадим.
Далее мы создаем наши шаблоны. Мы создаем папку templates
в папке src
для хранения наших шаблонов.
Там создайте blog-list-template.js
и добавьте:
import React from "react" import { graphql, Link } from "gatsby" import Layout from "../components/layout" export default class BlogList extends React.Component { constructor(props) { super(props) this.state = { pageNumArray: [1] } } componentDidMount() { this.setPageArray() } componentWillReceiveProps() { this.setPageArray() } setPageArray() { const totalCount = this.props.data.allMarkdownRemark.totalCount const postsPerPage = this.props.data.allMarkdownRemark.pageInfo.perPage let pageNumArray = Array.from( { length: Math.ceil(totalCount / postsPerPage) }, (v, i) => i + 1 ) this.setState({ pageNumArray }) } render() { const posts = this.props.data.allMarkdownRemark.edges const pathNames = this.props.location.pathname.split("/") const page = pathNames[pathNames.length - 1] return ( <Layout> {posts.map(({ node }) => { const title = node.frontmatter.title || node.fields.slug return ( <div key={node.fields.slug}> <h1>{title}</h1> <b>Date Posted: {node.frontmatter.date}</b> <div dangerouslySetInnerHTML={{ __html: node.html }} /> </div> ) })} <nav aria-label="Page navigation example"> <ul className="pagination"> {this.state.pageNumArray.map(p => ( <li className={`page-item ${page == p ? "active" : ""}`} key={p}> <Link className={`page-link`} to={`blog/page/${p}`}> {p} </Link> </li> ))} </ul> </nav> </Layout> ) } } export const blogListQuery = graphql` query blogListQuery($skip: Int!, $limit: Int!) { allMarkdownRemark( sort: { fields: [frontmatter___date], order: DESC } limit: $limit skip: $skip ) { edges { node { fields { slug } frontmatter { title date } html } } pageInfo { perPage } totalCount } } `
Здесь отобразите контент, который предоставляет запрос, который мы добавим в gatsby-node.js
.
blogListQuery
фильтрует содержимое, выполняя запрос и возвращая результаты. Затем результаты будут отображены в компоненте выше.
Функция setPageArray
получает номера страниц, получая общее количество страниц и затем устанавливая его в состояние компонента, чтобы у нас были ссылки для перехода на страницу, которая отображается с помощью:
{this.state.pageNumArray.map(p => ( <li className={`page-item ${page == p ? "active" : ""}`} key={p}> <Link className={`page-link`} to={`blog/page/${p}`}> {p} </Link> </li> ))}
Сообщения в блоге обрабатываются:
{posts.map(({ node }) => { const title = node.frontmatter.title || node.fields.slug return ( <div key={node.fields.slug}> <h1>{title}</h1> <b>Date Posted: {node.frontmatter.date}</b> <div dangerouslySetInnerHTML={{ __html: node.html }} /> </div> ) })}
Гэтсби очищает HTML, чтобы мы могли отображать его, задав его напрямую с помощью dangerouslySetInnerHTML
. Далее мы делаем шаблон для отображения постов с определенными тегами.
Для этого создаем tags.js
в папке templates
и добавляем:
import React from "react" import PropTypes from "prop-types" import Layout from "../components/layout" import _ from "lodash" // Components import { Link, graphql } from "gatsby" const Tags = ({ pageContext, data }) => { const { tag } = pageContext const { edges, totalCount } = data.allMarkdownRemark const tagHeader = `${totalCount} post${ totalCount === 1 ? "" : "s" } tagged with "${_.capitalize(tag)}"` return ( <Layout> <h1>{tagHeader}</h1> {edges.map(({ node }) => { const { slug } = node.fields const { title, date } = node.frontmatter return ( <div key={slug}> <h1>{title}</h1> <b>Date Posted: {date}</b> <div dangerouslySetInnerHTML={{ __html: node.html }} /> </div> ) })} </Layout> ) } Tags.propTypes = { pageContext: PropTypes.shape({ tag: PropTypes.string.isRequired, }), data: PropTypes.shape({ allMarkdownRemark: PropTypes.shape({ totalCount: PropTypes.number.isRequired, edges: PropTypes.arrayOf( PropTypes.shape({ node: PropTypes.shape({ frontmatter: PropTypes.shape({ title: PropTypes.string.isRequired, }), fields: PropTypes.shape({ slug: PropTypes.string.isRequired, }), }), }).isRequired ), }), }), } export default Tags export const pageQuery = graphql` query($tag: String) { allMarkdownRemark( limit: 2000 sort: { fields: [frontmatter___date], order: DESC } filter: { frontmatter: { tags: { in: [$tag] } } } ) { totalCount edges { node { fields { slug } frontmatter { title date } html } } } } `
Работает аналогично blog-list-template.js
. Запрос GraphQL внизу получает сообщения, помеченные данным тегом, а затем отображает их, запустив приведенный выше код компонента.
Метаданные наших страниц находятся в объекте frontmatter
каждой записи.
Затем в gatsby-config.js
мы заменяем существующий код на:
module.exports = { siteMetadata: { title: `Gatsby Blog`, description: `Gatsby Blog`, author: `@gatsbyjs`, }, plugins: [ `gatsby-plugin-react-helmet`, { resolve: `gatsby-source-filesystem`, options: { name: `images`, path: `${__dirname}/src/images`, }, }, `gatsby-transformer-sharp`, `gatsby-plugin-sharp`, { resolve: `gatsby-plugin-mdx`, options: { gatsbyRemarkPlugins: [ { resolve: `gatsby-remark-images`, options: { maxWidth: 1200, }, }, ], }, }, { resolve: `gatsby-plugin-manifest`, options: { name: `gatsby-starter-default`, short_name: `starter`, start_url: `/blog/page/1`, }, }, // this (optional) plugin enables Progressive Web App + Offline functionality // To learn more, visit: https://gatsby.dev/offline // `gatsby-plugin-offline`, { resolve: `gatsby-source-filesystem`, options: { name: `markdown-pages`, path: `${__dirname}/src/markdown-pages`, }, }, `gatsby-plugin-react-helmet`, { resolve: `gatsby-transformer-remark`, options: { plugins: [ { resolve: `gatsby-remark-images`, options: { // It's important to specify the maxWidth (in pixels) of // the content container as this plugin uses this as the // base for generating different widths of each image. maxWidth: 590, }, }, ], }, }, ], }
Чтобы все установленные нами пакеты запустились.
Наконец, в gatsby-node.js
мы заменяем существующий код на:
const _ = require("lodash") const path = require("path") const { createFilePath } = require("gatsby-source-filesystem") exports.createPages = async ({ graphql, actions, reporter }) => { const { createPage } = actions const result = await graphql( ` { postRemark: allMarkdownRemark( sort: { fields: [frontmatter___date], order: DESC } limit: 1000 ) { edges { node { fields { slug } } } } tagsGroup: allMarkdownRemark(limit: 2000) { group(field: frontmatter___tags) { fieldValue } } } ` ) if (result.errors) { reporter.panicOnBuild(`Error while running GraphQL query.`) return } // ... // Create blog-list pages const posts = result.data.postRemark.edges const postsPerPage = 5 const numPages = Math.ceil(posts.length / postsPerPage) Array.from({ length: numPages }).forEach((_, i) => { createPage({ path: `/blog/page/${i + 1}`, component: path.resolve("./src/templates/blog-list-template.js"), context: { limit: postsPerPage, skip: i * postsPerPage, numPages, currentPage: i + 1, }, }) }) const tags = result.data.tagsGroup.group tags.forEach(tag => { createPage({ path: `/tags/${_.kebabCase(tag.fieldValue)}/`, component: path.resolve("./src/templates/tags.js"), context: { tag: tag.fieldValue, }, }) }) } exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions if (node.internal.type === `MarkdownRemark`) { const value = createFilePath({ node, getNode }) createNodeField({ name: `slug`, node, value, }) } }
В приведенном выше файле у нас есть один запрос для получения всех сообщений и последующего создания страниц для их отображения в группах с помощью postRemark
запроса ниже:
postRemark: allMarkdownRemark( sort: { fields: [frontmatter___date], order: DESC } limit: 1000 ) { edges { node { fields { slug } } }
Мы создаем файлы страницы с помощью:
const posts = result.data.postRemark.edges const postsPerPage = 5 const numPages = Math.ceil(posts.length / postsPerPage) Array.from({ length: numPages }).forEach((_, i) => { createPage({ path: `/blog/page/${i + 1}`, component: path.resolve("./src/templates/blog-list-template.js"), context: { limit: postsPerPage, skip: i * postsPerPage, numPages, currentPage: i + 1, }, }) })
Он просматривает возвращенные результаты, и они направляют полученную запись в шаблон для создания страниц во время сборки.
tagsGroup
запрос ниже:
tagsGroup: allMarkdownRemark(limit: 2000) { group(field: frontmatter___tags) { fieldValue } }
Получает все теги, а затем передает теги в шаблон tags.js
, где он получает содержимое страницы и создает страницы во время сборки с сообщениями для данного тега на каждой странице.
Теперь мы готовы создать статический веб-сайт, запустив gatsby build
. Файлы должны быть собраны в папке public
.
После этого мы устанавливаем HTTP-сервер для обслуживания встроенных статических файлов. Мы установим пакеты http-server
Node.js. Запускаем npm i -g http-server
. Затем запустите http-server /path/to/gatsby-blog/public
.
После всего этого получаем: