gatsby_如何使用Gatsby和MDX从头开始构建编码博客

gatsby

I have been a Gatsby user since around version 0 back in May 2017.

2017年5月左右发布第0版以来,我一直是Gatsby用户。

Back then, I was using a template called Lumen. It was just what I needed at the time. Since then I have gone from using a template to creating my blog.

那时,我使用的模板叫做Lumen 。 当时正是我所需要的。 从那以后,我从使用模板转到了创建博客。

Over the years I have built my own Progressive Disclosure of Complexity with Gatsby to where I am now.

多年来,我已经与盖茨比建立了自己的渐进式复杂度披露体系 ,直至现在。

那是什么意思? (What does that mean?)

It means that although there are an awesome amount of Gatsby starters and themes out there to get you up and running in minutes, this post is going to focus on what you need to do to build your own blog. Starting with the most basic “Hello World!” to deploying your code to production.

这意味着,尽管那里有大量的盖茨比启动器和主题可以让您在数分钟内启动并运行,但本文将着重于构建自己的博客所需的工作。 从最基本的“ Hello World!”开始 将代码部署到生产中。

你要建造什么 (What you’re going to build)

You’re going to build a developer blog with MDX support (for some React components in Markdown goodness), so you will be able to add your own React components into your Markdown posts.

您将建立一个具有MDX支持的开发者博客(针对Markdown的一些React组件),因此您将能够在Markdown帖子中添加自己的React组件。

There’ll be:

将会有:

  • Adding a Layout

    添加布局
  • Basic styling with styled-components

    具有样式化组件的基本样式
  • Code blocks with syntax highlighting

    带有语法突出显示的代码块
  • Copy code snippet to clipboard

    将代码段复制到剪贴板
  • Cover images for the posts

    帖子的封面图片
  • Configuring an SEO component

    配置SEO组件
  • Deploying it to Netlify

    部署到Netlify

此操作方法适用于谁? (Who is this how-to for?)

People that may have used Gatsby before as a template and now want to get more involved in how to make changes.

以前可能使用Gatsby作为模板的人,现在希望更多地参与如何进行更改。

If you want to have code syntax highlighting.

如果要突出显示代码语法。

If you want to use styled-components in an app.

如果要在应用程序中使用样式化组件。

I really want to avoid this!

我真的很想避免这种情况!

要求 (Requirements)

You’re going to need a basic web development setup: node, terminal (bash, zsh or fish) and a text editor.

您将需要一个基本的Web开发设置:节点,终端(bash,zsh或fish)和文本编辑器。

I do like to use codesandbox.io for these sort of guides to reduce the barrier to entry but in this case I have found there are some limitations with starting out from scratch on codesandbox.io which doesn’t make this possible.

我确实喜欢将codesandbox.io用于此类指南,以减少进入的障碍,但是在这种情况下,我发现从codesandbox.io的白手起家存在一定的局限性,这无法实现。

I have made a guide on getting set up for web development with Windows Web-Dev Bootstrap and covered the same process in Ubuntu as well.

我已经为使用Windows Web-Dev Bootstrap进行Web开发设置做了一个指南,并且也介绍了Ubuntu中的相同过程。

Ok? Time to get started!

好? 是时候开始了!

你好,世界 (Hello World)

To kick this off with the Gatsby ‘hello world’, you’ll need to initialise the project with:

要使用盖茨比“ hello world”开始这一工作,您需要使用以下代码初始化该项目:

npm init -y
git init

I suggest that you commit this code to a git repository, so you should start with a .gitignore file.

我建议您将此代码提交到git存储库,因此您应该以.gitignore文件开头。

touch .gitignore

echo "# Project dependencies
.cache
node_modules

# Build directory
public

# Other
.DS_Store
yarn-error.log" > .gitignore

Ok now is a good time to do a git init and if you’re using VSCode you’ll see the changes reflected in the sidebar.

好了,现在是进行git init的好时机,如果您使用的是VSCode,您会在侧边栏中看到更改。

基本的问候世界 (basic hello world)

Ok a Gatsby hello world, get started with the bare minimum! Install the following:

好的,盖茨比世界你好,请从最低限度开始! 安装以下内容:

yarn add gatsby react react-dom

You’re going to need to create a pages directory and add an index file. You can do that in the terminal by typing the following:

您将需要创建一个页面目录并添加一个索引文件。 您可以在终端中输入以下命令来执行此操作:

# -p is to create parent directories too if needed
mkdir -p src/pages
touch src/pages/index.js

Now you can commence the hello word incantation! In the newly created index.js enter the following:

现在您可以开始打招呼了! 在新创建的index.js输入以下内容:

import React from 'react';

export default () => {
  return <h1>Hello World!</h1>;
};

Now you need to add the Gatsby develop script to the package.json file, -p specifies what port you want to run the project on and -o opens a new tab on your default browser, so in this case localhost:9988:

现在,您需要将Gatsby开发脚本添加到package.json文件, -p指定要在其上运行项目的端口,并且-o在默认浏览器上打开一个新标签,因此在本例中为localhost:9988

"dev": "gatsby develop -p 9988 -o"

And now it’s time to run the code! From the terminal type the npm script command you just created:

现在该运行代码了! 在终端中,输入您刚刚创建的npm脚本命令:

yarn dev
Note I’m using Yarn for installing all my dependencies and running scripts. If you prefer you can use npm, just bear in mind that the content on here uses yarn, so swap out commands where needed.
注意我正在使用Yarn来安装我的所有依赖项并运行脚本。 如果愿意,可以使用npm,请记住这里的内容使用yarn,因此请在需要的地方换出命令。

And with that the “Hello World” incantation is complete 🧙!

这样,“ Hello World”咒语就完成了!

添加内容 (Add content)

Now you have the base for your blog you’re going to want to add some content. First up we’re going to get the convention out of the way. For this how-to, the date format will be logical – the most logical way for a date format is YYYYMMDD, fight me!

现在,您已经有了博客的基础,您将要添加一些内容。 首先,我们要消除约定。 对于此操作方法,日期格式将是合乎逻辑的-日期格式最合乎逻辑的方式是YYYYMMDD ,打我!

So you’re going to structure your posts content in years. In each one of those you’re going to have another folder relating to the post with the (correct) date format for the beginning of the file followed by the title of the post.

因此,您将在几年内构建帖子内容。 在每个文件夹中,您都将拥有另一个与该帖子相关的文件夹,该文件夹的文件开头(日期)格式为(正确)日期,然后是该帖子的标题。

You could drill into this further if you like by separating out months and days. Depending on the volume of posts you've got going this may be a good approach. In this case and in the examples provided the convention detailed will be used.

如果您愿意,可以将月份和日期分开,以进一步深入研究。 根据您要去的帖子数量,这可能是一个好方法。 在这种情况下,在提供的示例中,将使用详细的约定。

# create multiple directories using curly braces
mkdir -p posts/2019/{2019-06-01-hello-world,2019-06-10-second-post,2019-06-20-third-post}
touch posts/2019/2019-06-01-hello-world/index.mdx
touch posts/2019/2019-06-10-second-post/index.mdx
touch posts/2019/2019-06-20-third-post/index.mdx

Ok that’s how to set up your posts. Now you need to add some content to them, so each file you have in here should have frontmatter. Frontmatter is a way to assign properties to the contents, in this case a title, published date and a published flag (true or false).

好的,这就是设置帖子的方法。 现在,您需要向其中添加一些内容,因此此处的每个文件都应具有前题。 Frontmatter是一种为内容分配属性的方法,在本例中为title ,发布datepublished标志( truefalse )。

---
title: Hello World - from mdx!
date: 2019-06-01
published: true
---

# h1 Heading

My first post!!

## h2 Heading

### h3 Heading
---
title: Second Post!
date: 2019-06-10
published: true
---

This is my second post!

#### h4 Heading

##### h5 Heading

###### h6 Heading
---
title: Third Post!
date: 2019-06-20
published: true
---

This is my third post!

> with a block quote!

盖茨比配置API (Gatsby config API)

Next, you’re going to configure Gatsby so that it can read your super awesome content you just created. First up you need to create a the gatsby-config.js file. In the terminal create the file:

接下来,您将配置Gatsby,以便它可以读取您刚刚创建的超棒内容。 首先,您需要创建一个gatsby-config.js文件。 在终端中创建文件:

touch gatsby-config.js

外挂程式 (Plugins)

And now you can add the plugins Gatsby needs to use for sourcing and displaying the the files you just created.

现在,您可以添加Gatsby用于采购和显示刚创建的文件所需的插件。

Gatsby源文件系统 (Gatsby source filesystem)

The gatsby-source-filesystem collects the files on the local filesystem for use in Gatsby once configured.

一旦配置好, gatsby-source-filesystem会收集本地文件系统上的文件,以供在Gatsby中使用。

Gatsby插件MDX (Gatsby plugin MDX)

The gatsby-plugin-mdx is what will be allowing us to write JSX in our Markdown documents and the heart of how the content is displayed in the blog.

gatsby-plugin-mdx是使我们能够在Markdown文档中编写JSX以及如何在博客中显示内容的核心。

Now is a good time to also add in dependent packages for the Gatsby plugin MDX which are @mdx-js/mdx and @mdx-js/react.

现在是时候为Gatsby插件MDX添加依赖包,即@mdx-js/mdx@mdx-js/react

In the terminal install the dependencies:

在终端中安装依赖项:

yarn add gatsby-plugin-mdx @mdx-js/mdx @mdx-js/react gatsby-source-filesystem

Now it's time to configure gatsby-config.js:

现在是时候配置gatsby-config.js

module.exports = {
  siteMetadata: {
    title: `The Localhost Blog`,
    description: `This is my coding blog where I write about my coding journey.`,
  },
  plugins: [
    {
      resolve: `gatsby-plugin-mdx`,
      options: {
        extensions: [`.mdx`, `.md`],
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/posts`,
        name: `posts`,
      },
    },
  ],
};

从GraphQL查询数据 (Query data from GraphQL)

Now you can see what the gatsby-source-filesystem and gatsby-plugin-mdx have done for us. You can now go to the Gatsby GraphQL GraphiQL explorer and check out the data:

现在您可以看到gatsby-source-filesystemgatsby-plugin-mdx为我们做了什么。 现在,您可以转到Gatsby GraphQL GraphiQL资源管理器并检查数据:

{
  allMdx {
    nodes {
      frontmatter {
        title
        date
      }
    }
  }
}

网站元数据 (Site Metadata)

When you want to reuse common pieces of data across the site (for example, your site title), you can store that data in siteMetadata. You touched on this when defining the gatsby-config.js, and now you’re going to separate this out from the module.exports. Why? It will be nicer to reason about once the config is filled with plugins.

当您想在整个站点上重用常见数据时(例如,您的站点标题),可以将这些数据存储在siteMetadata 。 您在定义gatsby-config.js时已涉及到此问题,现在您要将其与module.exports 。 为什么? 一旦配置充满了插件,就更好地进行推理了。

At the top of gatsby-config.js add a new object variable for the site metadata:

gatsby-config.js的顶部,为站点元数据添加一个新的对象变量:

const siteMetadata = {
  title: `The Localhost Blog`,
  description: `This is my coding blog where I write about my coding journey.`,
};

Now query the Site Metadata with GraphQL.

现在使用GraphQL查询站点元数据。

{
  site {
    siteMetadata {
      title
      description
    }
  }
}

网站元数据挂钩 (Site metadata hook)

Ok, so, that’s cool n’ all but how am I meant to use it? We'll do some of the code stuff and make a React hook so you can get your site data in any component you need it.

好的,这很酷,但是我打算怎么使用它呢? 我们将做一些代码工作,并制作一个React挂钩,以便您可以在需要的任何组件中获取站点数据。

Create a folder to keep all your hooks in and create a file for our hook. In the terminal do:

创建一个文件夹以保留所有挂钩,并为我们的挂钩创建一个文件。 在终端中执行:

mkdir src/hooks
touch src/hooks/useSiteMetadata.js

Ok, and in your newly created file were going to use the Gatsby useStaticQuery hook to make your own hook:

好的,在您新创建的文件中,将使用Gatsby useStaticQuery挂钩创建您自己的挂钩:

import { graphql, useStaticQuery } from 'gatsby';

export const useSiteMetadata = () => {
  const { site } = useStaticQuery(
    graphql`
      query SITE_METADATA_QUERY {
        site {
          siteMetadata {
            title
            description
          }
        }
      }
    `
  );
  return site.siteMetadata;
};

Now you can use this hook anywhere in your site, so do that now in src/pages/index.js:

现在,您可以在网站的任何地方使用此挂钩,因此现在在src/pages/index.js

import React from 'react';
import { useSiteMetadata } from '../hooks/useSiteMetadata';

export default () => {
  const { title, description } = useSiteMetadata();
  return (
    <>
      <h1>{title}</h1>
      <p>{description}</p>
    </>
  );
};

造型 (Styling)

You’re going to use styled-components for styling. Styled-components (for me) help with scoping styles in your components. Time to go over the basics now.

您将使用样式化组件进行样式化。 样式化组件(对我而言)有助于在组件中定义作用域样式。 现在该讨论基础知识了。

安装样式化的组件 (install styled-components)

yarn add gatsby-plugin-styled-components styled-components babel-plugin-styled-components

So, what was all that I just installed?

那么,我刚刚安装的是什么?

The babel plugin is for automatic naming of components to help with debugging.

babel插件用于自动命名组件,以帮助调试。

The Gatsby plugin is for built-in server-side rendering support.

Gatsby插件用于内置的服务器端渲染支持。

配置 (Configure)

Ok, with that detailed explanation out of the way, configure them in gatsby-config.js:

好的,有了详细的解释,请在gatsby-config.js配置:

const siteMetadata = {
  title: `The Localhost Blog`,
  description: `This is my coding blog where I write about my coding journey.`,
};

module.exports = {
  siteMetadata: siteMetadata,
  plugins: [
    `gatsby-plugin-styled-components`,
    {
      resolve: `gatsby-plugin-mdx`,
      options: {
        extensions: [`.mdx`, `.md`],
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: { path: `${__dirname}/posts`, name: `posts` },
    },
  ],
};

Time to go over a styled component. In  index.js you’re going to import styled from 'styled-components' and create a StyledH1 variable.

是时候讨论样式化组件了。 在index.jsimport styled from 'styled-components'并创建一个StyledH1变量。

So, you’re using the variable to wrap your {title} that you’re destructuring from the useSiteMetadata hook you made previously.

因此,您正在使用变量来包装要从先前创建的useSiteMetadata钩子中解构的{title}

For this example make it the now iconic Gatsby rebeccapurple.

对于此示例,使它成为现在具有标志性的Gatsby rebeccapurple

import React from 'react';
import styled from 'styled-components';
import { useSiteMetadata } from '../hooks/useSiteMetadata';

const StyledH1 = styled.h1`
  color: rebeccapurple;
`;

export default () => {
  const { title, description } = useSiteMetadata();
  return (
    <>
      <StyledH1>{title}</StyledH1>
      <p>{description}</p>
    </>
  );
};

That is styled-components on a very basic level. Basically create the styling you want for your page elements you’re creating in the JSX.

这是非常基本的样式化组件。 基本上,为要在JSX中创建的页面元素创建所需的样式。

布局 (Layout)

Gatsby doesn’t apply any layouts by default but instead uses the way you can compose React components for the layout. This means it’s up to you how you want to layout what you're building with Gatsby.

Gatsby默认情况下不应用任何布局,而是使用您可以为布局组成React组件的方式。 这意味着您可以根据自己的意愿来布局Gatsby。

In this guide we're going to initially create a basic layout component that you’ll add to as you go along. For more detail on layout components take a look at the Gatsby layout components page.

在本指南中,我们将首先创建一个基本的布局组件,然后将其添加到其中。 有关布局组件的更多详细信息,请查看Gatsby 布局组件页面。

Now you’re going to refactor the home page (src/pages/index.js) a little and make some components for your blog layout and header. In the terminal create a components directory and a Header and Layout component:

现在,您将对主页( src/pages/index.js )进行一些重构,并为博客布局和标题创建一些组件。 在终端中,创建一个组件目录以及一个Header and Layout组件:

mkdir src/components
touch src/components/Header.js src/components/Layout.js

Now to move the title and description from src/pages/index.js to the newly created src/components/Header.js component, destructuring props for the siteTitle and siteDescription, you’ll pass these from the Layout component to here. You’re going to add Gatsby Link to this so users can click on the header to go back to the home page.

现在,要将标题和描述从src/pages/index.js移至新创建的src/components/Header.js组件,为siteTitlesiteDescription道具,您将把它们从Layout组件传递到此处。 您将为此添加Gatsby Link,以便用户可以单击标题返回首页。

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

export const Header = ({ siteTitle, siteDescription }) => (
  <Link to="/">
    <h1>{siteTitle}</h1>
    <p>{siteDescription}</p>
  </Link>
);

Now to the Layout component: this is going to be a basic wrapper component for now. You’re going to use your site metadata hook for the title and description and pass them to the header component and return the children of the wrapper (Layout).

现在到Layout组件:这将是目前的基本包装器组件。 您将使用站点元数据挂钩获取标题和描述,并将其传递给标题组件,并返回包装器的子代( Layout )。

import React from 'react';
import { useSiteMetadata } from '../hooks/useSiteMetadata';
import { Header } from './Header';

export const Layout = ({ children }) => {
  const { title, description } = useSiteMetadata();
  return (
    <>
      <Header siteTitle={title} siteDescription={description} />
      {children}
    </>
  );
};

Now to add the slightest of styles for some alignment for src/components/Layout.js, create an AppStyles styled component and make it the main wrapper of your Layout.

现在,要为src/components/Layout.js添加一些样式的对齐方式,请创建一个AppStyles样式的组件,并将其作为Layout的主要包装。

import React from 'react';
import styled from 'styled-components';
import { useSiteMetadata } from '../hooks/useSiteMetadata';
import { Header } from './Header';

const AppStyles = styled.main`
  width: 800px;
  margin: 0 auto;
`;

export const Layout = ({ children }) => {
  const { title, description } = useSiteMetadata();
  return (
    <AppStyles>
      <Header siteTitle={title} siteDescription={description} />
      {children}
    </AppStyles>
  );
};

Ok, now refactor your homepage (src/pages/index.js) with Layout.

好的,现在使用Layout重构您的首页( src/pages/index.js )。

import React from 'react';
import { Layout } from '../components/Layout';

export default () => {
  return (
    <>
      <Layout />
    </>
  );
};

索引页帖子查询 (Index page posts query)

Now you can take a look at getting some of the posts you’ve created added to the index page of your blog. You’re going to do that by creating a GraphQL query to list out the posts by title, order by date, and add an excerpt of the post.

现在,您可以看看将您创建的一些帖子添加到博客的索引页面。 为此,您将创建一个GraphQL查询,以按标题,按日期排序列出帖子,并添加该帖子的摘录。

The query will look something like this:

查询将如下所示:

{
  allMdx {
    nodes {
      id
      excerpt(pruneLength: 250)
      frontmatter {
        title
        date
      }
    }
  }
}

If you put that into the GraphiQL GUI, though, you’ll notice that the posts aren’t in any given order. So now add a sort to this - and you’ll also add in a filter for posts that are marked as published or not.

但是,如果将其放到GraphiQL GUI中,您会注意到发布的顺序不是给定的。 因此,现在为此添加一个排序-您还将为标记为已发布或未发布的帖子添加一个过滤器。

{
  allMdx(
    sort: { fields: [frontmatter___date], order: DESC }
    filter: { frontmatter: { published: { eq: true } } }
  ) {
    nodes {
      id
      excerpt(pruneLength: 250)
      frontmatter {
        title
        date
      }
    }
  }
}

On the homepage (src/pages/index.js) you’re going to use the query we just put together to get a list of published posts in date order; add the following to the index.js file:

在首页( src/pages/index.js )上,您将使用我们刚刚汇总的查询来按日期顺序获取已发布帖子的列表; 将以下内容添加到index.js文件:

import { graphql } from 'gatsby';
import React from 'react';
import { Layout } from '../components/Layout';

export default ({ data }) => {
  return (
    <>
      <Layout>
        {data.allMdx.nodes.map(({ excerpt, frontmatter }) => (
          <>
            <h1>{frontmatter.title}</h1>
            <p>{frontmatter.date}</p>
            <p>{excerpt}</p>
          </>
        ))}
      </Layout>
    </>
  );
};

export const query = graphql`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date
        }
      }
    }
  }
`;

Woah! WTF was all that yo!?

哇! WTF就是全部!!

Ok, you’re looping through the data passed into the component via the GraphQL query. Gatsby graphql runs the query (SITE_INDEX_QUERY) at runtime and gives us the results as props to your component via the data prop.

好的,您正在遍历通过GraphQL查询传递到组件的数据。 Gatsby graphql在运行时运行查询( SITE_INDEX_QUERY ),并通过data SITE_INDEX_QUERY将结果作为SITE_INDEX_QUERY提供给组件。

子弹和路径 (Slugs and Paths)

Gatsby source filesystem will help with the creation of slugs (URL paths for the posts you’re creating). In Gatsby node you’re going to create the slugs for your posts.

Gatsby源文件系统将有助于创建子弹(正在创建的帖子的URL路径)。 在Gatsby节点中,您将为帖子创建标签。

First up you’re going to need to create a gatsby-node.js file:

首先,您需要创建一个gatsby-node.js文件:

touch gatsby-node.js

This will create the file path (URL) for each of the blog posts.

这将为每个博客文章创建文件路径(URL)。

You’re going to be using the Gatsby Node API onCreateNode and destructuring out node, actions and getNode for use in creating the file locations and associated value.

您将使用Gatsby Node API onCreateNode并解构出nodeactionsgetNode以用于创建文件位置和相关值。

const { createFilePath } = require(`gatsby-source-filesystem`);

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions;
  if (node.internal.type === `Mdx`) {
    const value = createFilePath({ node, getNode });
    createNodeField({
      name: `slug`,
      node,
      value,
    });
  }
};

Now to help visualise some of the data being passed into the components you’re going to use Dump.js for debugging the data. Thanks to Wes Bos for the super handy Dump.js component.

现在,为了帮助可视化传递到组件中的某些数据,您将使用Dump.js调试数据。 感谢Wes Bos提供了超级方便的Dump.js组件。

To get the component set up, create a Dump.js file in your src\components folder and copypasta the code from the linked GitHub page.

要设置组件,请在src\components文件夹中创建一个Dump.js文件,然后从链接的GitHub页面复制代码。

touch /src/components/Dump.js
import React from 'react';

const Dump = props => (
  <div
    style={{
      fontSize: 20,
      border: '1px solid #efefef',
      padding: 10,
      background: 'white',
    }}>
    {Object.entries(props).map(([key, val]) => (
      <pre key={key}>
        <strong style={{ color: 'white', background: 'red' }}>
          {key} 💩
        </strong>
        {JSON.stringify(val, '', ' ')}
      </pre>
    ))}
  </div>
);

export default Dump;

Now you can use the Dump component anywhere in your project. To demonstrate, use it with the index page data to see the output.

现在,您可以在项目中的任何地方使用Dump组件。 为了演示,将它与索引页data一起使用以查看输出。

So in the src/pages/index.js you’re going to import the Dump component and pass in the data prop and see what the output looks like.

因此,在src/pages/index.js您将导入D​​ump组件并传入data属性,然后查看输出结果。

import { graphql } from 'gatsby';
import React from 'react';
import Dump from '../components/Dump';
import { Layout } from '../components/Layout';

export default ({ data }) => {
  return (
    <>
      <Layout>
        <Dump data={data} />
        {data.allMdx.nodes.map(({ excerpt, frontmatter }) => (
          <>
            <h1>{frontmatter.title}</h1>
            <p>{frontmatter.date}</p>
            <p>{excerpt}</p>
          </>
        ))}
      </Layout>
    </>
  );
};

export const query = graphql`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date
        }
      }
    }
  }
`;

Now you’ve created the paths you can link to them with Gatsby Link. First you’ll need to add the slug to your SITE_INDEX_QUERY Then you can add gatsby Link to src/pages/index.js.

现在,您已经创建了可以使用Gatsby Link链接到它们的路径。 首先,您需要将块添加到SITE_INDEX_QUERY然后可以将gatsby Link添加到src/pages/index.js

You’re also going to create some styled-components for wrapping the list of posts and each individual post as well.

您还将创建一些样式化的组件,用于包装帖子列表以及每个帖子。

import { graphql, Link } from 'gatsby';
import React from 'react';
import styled from 'styled-components';
import { Layout } from '../components/Layout';

const IndexWrapper = styled.main``;

const PostWrapper = styled.div``;

export default ({ data }) => {
  return (
    <Layout>
      <IndexWrapper>
        {data.allMdx.nodes.map(
          ({ id, excerpt, frontmatter, fields }) => (
            <PostWrapper key={id}>
              <Link to={fields.slug}>
                <h1>{frontmatter.title}</h1>
                <p>{frontmatter.date}</p>
                <p>{excerpt}</p>
              </Link>
            </PostWrapper>
          )
        )}
      </IndexWrapper>
    </Layout>
  );
};

export const query = graphql`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date
        }
        fields {
          slug
        }
      }
    }
  }
`;

添加博客文章模板 (Adding a Blog Post Template)

Now you have the links pointing to the blog posts you currently have no file associated with the path. This means that clicking a link will give you a 404 and the built-in gatsby 404 will list all the pages available in the project, currently only the / index/homepage.

现在,您具有指向博客帖子的链接,您当前没有与该路径关联的文件。 这意味着单击链接将为您提供404,而内置的gatsby 404将列出项目中可用的所有页面,当前仅列出/ index / homepage。

So, for each one of your blog posts you’re going to use a template that will contain the information you need to make up your blog post. To start, create a templates directory and template file for that with:

因此,对于您的每篇博客文章,您将使用一个模板,其中包含组成您的博客文章所需的信息。 首先,使用以下命令创建一个templates目录和模板文件:

mkdir -p src/templates
touch src/templates/blogPostTemplate.js

For now you’re going to scaffold out a basic template (you’ll be adding data to this shortly):

现在,您将要搭建一个基本模板(稍后将向其中添加数据):

import React from 'react';

export default () => {
  return (
    <>
      <p>post here</p>
    </>
  );
};

To populate the template you’ll need to use Gatsby node to create your pages.

要填充模板,您需要使用Gatsby节点创建页面。

Gatsby Node has many internal APIs available to us. For this example you’re going to be using the createPages API.

Gatsby Node提供了许多内部API。 在此示例中,您将使用createPages API。

More info on Gatsby createPages API can be found on the Gatsby docs, details here: https://www.gatsbyjs.org/docs/node-apis/#createPages

可以在Gatsby文档上找到有关Gatsby createPages API的更多信息,详情请参见: https : //www.gatsbyjs.org/docs/node-apis/#createPages

In your gatsby-node.js file you’re going to add in the following in addition to the onCreateNode export you did earlier.

gatsby-node.js文件中,除了您之前执行的onCreateNode导出外,还将添加以下内容。

const { createFilePath } = require(`gatsby-source-filesystem`);
const path = require(`path`);

exports.createPages = ({ actions, graphql }) => {
  const { createPage } = actions;
  const blogPostTemplate = path.resolve(
    'src/templates/blogPostTemplate.js'
  );

  return graphql(`
    {
      allMdx {
        nodes {
          fields {
            slug
          }
          frontmatter {
            title
          }
        }
      }
    }
  `).then(result => {
    if (result.errors) {
      throw result.errors;
    }

    const posts = result.data.allMdx.nodes;

    // create page for each mdx file
    posts.forEach(post => {
      createPage({
        path: post.fields.slug,
        component: blogPostTemplate,
        context: {
          slug: post.fields.slug,
        },
      });
    });
  });
};

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions;
  if (node.internal.type === `Mdx`) {
    const value = createFilePath({ node, getNode });
    createNodeField({
      name: `slug`,
      node,
      value,
    });
  }
};

The part that you need to pay particular attention to right now is the .forEach loop where you’re using the createPage function we destructured from the actions object.

现在,您需要特别注意的部分是.forEach循环,您将在其中使用从actions对象中解构的createPage函数。

This is where you pass the data needed by blogPostTemplate you defined earlier. You’re going to be adding more to the context for post navigation soon.

这是您传递先前定义的blogPostTemplate所需的数据的地方。 您将很快为context添加更多内容以进行导航。

// create page for each mdx node
posts.forEach(post => {
  createPage({
    path: post.fields.slug,
    component: blogPostTemplate,
    context: {
      slug: post.fields.slug,
    },
  });
});

建立博客帖子模板 (Build out Blog Post Template)

Now you’re going to take the context information passed to the blogPostTemplate.js to make the blog post page.

现在,您将使用传递到blogPostTemplate.js的上下文信息来制作博客文章页面。

This is similar to the index.js homepage, whereas there’s GraphQL data used to create the page. But in this instance the template uses a variable (also known as a parameter or an identifier) so you can query data specific to that given variable.

这类似于index.js主页,但是有用于创建页面的GraphQL数据。 但是在这种情况下,模板使用变量(也称为参数或标识符),因此您可以查询特定于该给定变量的数据。

Now quickly dig into that with a demo. In the GraphiQL GUI, create a named query and define the variable you’re going to pass in:

现在,通过演示快速深入探讨。 在GraphiQL GUI中,创建一个命名查询并定义要传递的变量:

query PostBySlug($slug: String!) {
  mdx(fields: { slug: { eq: $slug } }) {
    frontmatter {
      title
      date(formatString: "YYYY MMMM Do")
    }
  }
}

Here you’re defining the variable as slug with the $ denoting that it’s a variable. You also need to define the variable type as (in this case) String!. The exclamation after the type means that it has to be a string being passed into the query.

在这里,您将变量定义为“ slug”,用$表示它是变量。 您还需要将变量类型定义为(在这种情况下) String! 。 类型后的感叹号意味着它必须是要传递到查询中的字符串。

Using mdx you’re going to filter on fields where the slug matches the variable being passed into the query.

使用mdx您将过滤fields slug与传递到查询中的变量匹配的fields

Running the query now will show an error as there’s no variable being fed into the query. If you look to the bottom of the query pane you should notice QUERY VARIABLES. Click on that to bring up the variables pane.

现在运行查询将显示错误,因为没有变量输入查询。 如果查看查询窗格的底部,您应该会注意到QUERY VARIABLES 。 单击该按钮以显示变量窗格。

This is where you can add in one of the post paths you created earlier. If you have your dev server up and running, go to one of the posts and take the path and paste it into the quotes "" and try running the query again.

您可以在这里添加您先前创建的发布路径之一。 如果您的开发服务器已启动并正在运行,请转至其中一篇文章,并找到路径并将其粘贴到引号""然后尝试再次运行查询。

{
  "slug": "/2019/2019-06-20-third-post/"
}

Time to use that data to make the post. You’re going to add body to the query and have that at the bottom of your page file.

是时候使用这些数据进行发布了。 您将在查询中添加body ,并将其放在页面文件的底部。

Right now you’re going to create a simple react component that will display the data you have queried.

现在,您将创建一个简单的react组件,该组件将显示您查询的数据。

Destructuring the frontmatter and body from the GraphQL query, you’ll get the Title and the Data from the frontmatter object and wrap the body in the MDXRenderer.

解构的frontmatterbody从GraphQL查询,你会得到的标题,并从frontmatter对象中的数据和包裹bodyMDXRenderer

import { graphql } from 'gatsby';
import { MDXRenderer } from 'gatsby-plugin-mdx';
import React from 'react';
import { Layout } from '../components/Layout';

export default ({ data }) => {
  const { frontmatter, body } = data.mdx;
  return (
    <Layout>
      <h1>{frontmatter.title}</h1>
      <p>{frontmatter.date}</p>
      <MDXRenderer>{body}</MDXRenderer>
    </Layout>
  );
};

export const query = graphql`
  query PostsBySlug($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      body
      frontmatter {
        title
        date(formatString: "YYYY MMMM Do")
      }
    }
  }
`;

If you haven’t done so already now would be a good time to restart your dev server.

如果您现在还没有这样做,那么现在是重新启动开发服务器的好时机。

Now you can click on one of the post links and see your blog post template in all it’s basic glory!

现在,您可以单击其中一个帖子链接,并在所有基本荣耀中查看您的博客帖子模板!

上一个和下一个导航 (Previous and Next navigation)

Coolio! Now you have your basic blog where you can list available posts and click a link to see the full post in a predefined template. Once you’re in a post you have to navigate back to the home page to pick out a new post to read. In this section you’re going to work on adding in some previous and next navigation.

Coolio! 现在,您有了基本的博客,可以在其中列出可用的帖子,然后单击链接以在预定义的模板中查看完整的帖子。 张贴文章后,您必须导航回到首页以挑选新文章阅读。 在本节中,您将继续添加上一个和下一个导航。

Remember the .forEach snippet you looked at earlier? That’s where you’re going to pass some additional context to the page by selecting out the previous and next posts.

还记得您之前看过的.forEach代码段吗? 那就是您要通过选择上一篇和下一篇文章将一些其他上下文传递给页面的地方。

// create page for each mdx node
posts.forEach((post, index) => {
  const previous =
    index === posts.length - 1 ? null : posts[index + 1];
  const next = index === 0 ? null : posts[index - 1];

  createPage({
    path: post.fields.slug,
    component: blogPostTemplate,
    context: {
      slug: post.fields.slug,
      previous,
      next,
    },
  });
});

So this should now match up with the query you have on the homepage (src/pages/index.js) except you currently have no filter or sort applied here. So do that now in gatsby-node.js and apply the same filters as on the homepage query:

因此,这应该与您在主页( src/pages/index.js )上进行的查询匹配,但当前您没有在此处应用过滤器或排序。 因此,现在在gatsby-node.js执行此操作,并应用与主页查询相同的过滤器:

const { createFilePath } = require(`gatsby-source-filesystem`);
const path = require(`path`);

exports.createPages = ({ actions, graphql }) => {
  const { createPage } = actions;
  const blogPostTemplate = path.resolve(
    'src/templates/blogPostTemplate.js'
  );

  return graphql(`
    {
      allMdx(
        sort: { fields: [frontmatter___date], order: DESC }
        filter: { frontmatter: { published: { eq: true } } }
      ) {
        nodes {
          fields {
            slug
          }
          frontmatter {
            title
          }
        }
      }
    }
  `).then(result => {
    if (result.errors) {
      throw result.errors;
    }

    const posts = result.data.allMdx.nodes;

    // create page for each mdx node
    posts.forEach((post, index) => {
      const previous =
        index === posts.length - 1 ? null : posts[index + 1];
      const next = index === 0 ? null : posts[index - 1];

      createPage({
        path: post.fields.slug,
        component: blogPostTemplate,
        context: {
          slug: post.fields.slug,
          previous,
          next,
        },
      });
    });
  });
};

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions;
  if (node.internal.type === `Mdx`) {
    const value = createFilePath({ node, getNode });
    createNodeField({
      name: `slug`,
      node,
      value,
    });
  }
};

Now you will be able to expose the previous and next objects passed in as context from Gatsby node.

现在,您将能够公开从Gatsby节点作为上下文传入的previousnext对象。

You can destructure previous and next from pageContext and for now pop them into your super handy Dump component to take a look at their contents.

您可以从pageContext解构previousnext ,现在将它们弹出到超级方便的Dump组件中,以查看其内容。

import { graphql } from 'gatsby';
import { MDXRenderer } from 'gatsby-plugin-mdx';
import React from 'react';
import Dump from '../components/Dump';
import { Layout } from '../components/Layout';

export default ({ data, pageContext }) => {
  const { frontmatter, body } = data.mdx;
  const { previous, next } = pageContext;
  return (
    <Layout>
      <Dump previous={previous} />
      <Dump next={next} />
      <h1>{frontmatter.title}</h1>
      <p>{frontmatter.date}</p>
      <MDXRenderer>{body}</MDXRenderer>
    </Layout>
  );
};

export const query = graphql`
  query PostsBySlug($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      body
      frontmatter {
        title
        date(formatString: "YYYY MMMM Do")
      }
    }
  }
`;

Add in previous and next navigation, this is a couple of ternary operations. If the variable is empty then return null else render a Gatsby Link component with the page slug and the frontmatter title:

在上一个和下一个导航中添加,这是几个三元操作。 如果变量为空,则返回null否则使用页面slug和frontmatter标题呈现Gatsby Link组件:

import { graphql, Link } from 'gatsby';
import { MDXRenderer } from 'gatsby-plugin-mdx';
import React from 'react';
import Dump from '../components/Dump';
import { Layout } from '../components/Layout';

export default ({ data, pageContext }) => {
  const { frontmatter, body } = data.mdx;
  const { previous, next } = pageContext;
  return (
    <Layout>
      <Dump previous={previous} />
      <Dump next={next} />
      <h1>{frontmatter.title}</h1>
      <p>{frontmatter.date}</p>
      <MDXRenderer>{body}</MDXRenderer>
      {previous === false ? null : (
        <>
          {previous && (
            <Link to={previous.fields.slug}>
              <p>{previous.frontmatter.title}</p>
            </Link>
          )}
        </>
      )}
      {next === false ? null : (
        <>
          {next && (
            <Link to={next.fields.slug}>
              <p>{next.frontmatter.title}</p>
            </Link>
          )}
        </>
      )}
    </Layout>
  );
};

export const query = graphql`
  query PostsBySlug($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      body
      frontmatter {
        title
        date(formatString: "YYYY MMMM Do")
      }
    }
  }
`;

代码块 (Code Blocks)

Now to add some syntax highlighting for adding code blocks to your blog pages. To do that you’re going to add dependencies for prism-react-renderer and react-live. You’ll also create the files you’re going to need to use:

现在添加一些语法突出显示,以向您的博客页面添加代码块。 为此,您将为Prism-react-rendererreact-live添加依赖项。 您还将创建需要使用的文件:

yarn add prism-react-renderer react-live
touch root-wrapper.js gatsby-ssr.js gatsby-browser.js

You’ll come onto react-live soon. For now, you’re going to get prism-react-render up and running for syntax highlighting for any code you’re going to add to the blog. But before that you’re going to go over the root wrapper concept.

您很快就会上react-live 。 目前,您将启动并进行prism-react-render ,以对要添加到博客的任何代码进行语法高亮显示。 但是在此之前,您将了解根包装器的概念。

So, to change the rendering of a page element, such as a heading or a code block, you’re going to need to use the MDXProvider. The MDXProvider is a component you can use anywhere higher in the React component tree than the MDX content you want to render.

因此,要更改页面元素(例如标题或代码块)的呈现方式,您将需要使用MDXProviderMDXProvider是一个组件,您可以在React组件树中使用的位置高于您要呈现的MDX内容的位置。

Gatsby browser and a Gatsby SSR both have wrapRootElement available to them and that is as high up the tree as you can get. So you’re going to create the root-wrapper.js file and add elements you want to override there and import it into both gatsby-browser.js and gatsby-ssr.js so you’re not duplicating code.

Gatsby浏览器和Gatsby SSR都可以使用wrapRootElement ,并且它们在树的wrapRootElement 。 因此,您将创建root-wrapper.js文件,并在其中添加要覆盖的元素,然后将其导入gatsby-browser.jsgatsby-ssr.js以免重复代码。

Before you go any further I want to add that there is a top quality egghead.io playlist resource for using MDX with Gatsby by Chris Chris Biscardi. There’s a ton of useful information in there on MDX in Gatsby.

在继续之前,我想补充一下,Chris Chris Biscardi 编写了一个高质量的egghead.io播放列表资源,用于将MDX与Gatsby一起使用。 关于盖茨比(Gatsby)的MDX,这里有大量有用的信息。

Ok, first up you’re going to import the root-wrapper.js file into both gatsby-browser.js and gatsby-ssr.js. Into both code modules paste the following:

好的,首先,您将把root-wrapper.js文件导入到gatsby-browser.jsgatsby-ssr.js 。 在两个代码模块中粘贴以下内容:

import { wrapRootElement as wrap } from './root-wrapper';

export const wrapRootElement = wrap;

Ok, now you can work on the code that will be used in both modules. MDX allows you to control the rendering of page elements in your markdown. MDXProvider is used to give React components to override the markdown page elements.

好的,现在您可以处理将在两个模块中使用的代码。 MDX允许您控制Markdown中页面元素的呈现。 MDXProvider用于给React组件覆盖markdown页面元素。

Quick demonstration, in root-wrapper.js add the following:

快速演示,在root-wrapper.js添加以下内容:

import { MDXProvider } from '@mdx-js/react';
import React from 'react';

const components = {
  h2: ({ children }) => (
    <h2 style={{ color: 'rebeccapurple' }}>{children}</h2>
  ),
  'p.inlineCode': props => (
    <code style={{ backgroundColor: 'lightgray' }} {...props} />
  ),
};

export const wrapRootElement = ({ element }) => (
  <MDXProvider components={components}>{element}</MDXProvider>
);

You’re now overriding any h2 in your rendered markdown along with any code blocks (that’s words wrapped in `backticks`).

现在,您将覆盖渲染的markdown中的所有h2以及任何code块(即包装在`backticks`中的单词)。

Ok, now for the syntax highlighting, create a post with a block of code in it:

好的,现在要突出显示语法,创建一个包含代码块的帖子:

mkdir posts/2019-07-01-code-blocks
touch posts/2019-07-01-code-blocks/index.mdx

Paste in some content:

粘贴一些内容:

---
title: Code Blocks
date: 2019-07-01
published: true
---

## Yes! Some code!

Here is the `Dump` component!

```jsx
import React from 'react';

const Dump = props => (
  <div
    style={{
      fontSize: 20,
      border: '1px solid #efefef',
      padding: 10,
      background: 'white',
    }}>
    {Object.entries(props).map(([key, val]) => (
      <pre key={key}>
        <strong style={{ color: 'white', background: 'red' }}>
          {key} 💩
        </strong>
        {JSON.stringify(val, '', ' ')}
      </pre>
    ))}
  </div>
);

export default Dump;
```

Go to the prism-react-renderer GitHub page and copy the example code into root-wrapper.js for the pre element.

转到Prism-react-renderer GitHub页面,并将示例代码复制到root-wrapper.js中作为pre元素。

You’re going to copy the provided code for highlighting to validate that it works.

您将复制提供的代码以突出显示以验证其是否有效。

import { MDXProvider } from '@mdx-js/react';
import Highlight, { defaultProps } from 'prism-react-renderer';
import React from 'react';

const components = {
  h2: ({ children }) => (
    <h2 style={{ color: 'rebeccapurple' }}>{children}</h2>
  ),
  'p.inlineCode': props => (
    <code style={{ backgroundColor: 'lightgray' }} {...props} />
  ),
  pre: props => (
    <Highlight
      {...defaultProps}
      code={`
        (function someDemo() {
          var test = "Hello World!";
          console.log(test);
        })();

        return () => <App />;
      `}
      language="jsx">
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) => (
        <pre className={className} style={style}>
          {tokens.map((line, i) => (
            <div {...getLineProps({ line, key: i })}>
              {line.map((token, key) => (
                <span {...getTokenProps({ token, key })} />
              ))}
            </div>
          ))}
        </pre>
      )}
    </Highlight>
  ),
};

export const wrapRootElement = ({ element }) => (
  <MDXProvider components={components}>{element}</MDXProvider>
);

Cool, cool! Now you want to replace the pasted in code example with the props of the child component of the pre component. You can do that with props.children.props.children.trim() 🙃.

酷,酷! 现在,您要用pre组件的child组件的props替换粘贴在代码示例中的代码。 您可以使用props.children.props.children.trim() that做到这一点。

import { MDXProvider } from '@mdx-js/react';
import Highlight, { defaultProps } from 'prism-react-renderer';
import React from 'react';

const components = {
  pre: props => (
    <Highlight
      {...defaultProps}
      code={props.children.props.children.trim()}
      language="jsx">
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) => (
        <pre className={className} style={style}>
          {tokens.map((line, i) => (
            <div {...getLineProps({ line, key: i })}>
              {line.map((token, key) => (
                <span {...getTokenProps({ token, key })} />
              ))}
            </div>
          ))}
        </pre>
      )}
    </Highlight>
  ),
};

export const wrapRootElement = ({ element }) => (
  <MDXProvider components={components}>{element}</MDXProvider>
);

Then to match the language, for now you’re going to add in a matches function to match the language class assigned to the code block.

然后要匹配语言,现在您将添加一个matches函数以匹配分配给代码块的语言类。

import { MDXProvider } from '@mdx-js/react';
import Highlight, { defaultProps } from 'prism-react-renderer';
import React from 'react';

const components = {
  h2: ({ children }) => (
    <h2 style={{ color: 'rebeccapurple' }}>{children}</h2>
  ),
  'p.inlineCode': props => (
    <code style={{ backgroundColor: 'lightgray' }} {...props} />
  ),
  pre: props => {
    const className = props.children.props.className || '';
    const matches = className.match(/language-(?<lang>.*)/);
    return (
      <Highlight
        {...defaultProps}
        code={props.children.props.children.trim()}
        language={
          matches && matches.groups && matches.groups.lang
            ? matches.groups.lang
            : ''
        }>
        {({
          className,
          style,
          tokens,
          getLineProps,
          getTokenProps,
        }) => (
          <pre className={className} style={style}>
            {tokens.map((line, i) => (
              <div {...getLineProps({ line, key: i })}>
                {line.map((token, key) => (
                  <span {...getTokenProps({ token, key })} />
                ))}
              </div>
            ))}
          </pre>
        )}
      </Highlight>
    );
  },
};

export const wrapRootElement = ({ element }) => (
  <MDXProvider components={components}>{element}</MDXProvider>
);

prism-react-renderer comes with additional themes over the default theme which is duotoneDark. You’re going to use nightOwl in this example, but feel free to take a look at the other examples if you like.

pyramid -react-renderer随附了默认主题duotoneDark之外的其他主题。 在本示例中,您将使用nightOwl ,但如果愿意,可以随时查看其他示例

Import the theme then use it in the props of the Highlight component.

导入theme然后在“ Highlight组件的道具中使用它。

import { MDXProvider } from '@mdx-js/react';
import Highlight, { defaultProps } from 'prism-react-renderer';
import theme from 'prism-react-renderer/themes/nightOwl';
import React from 'react';

const components = {
  pre: props => {
    const className = props.children.props.className || '';
    const matches = className.match(/language-(?<lang>.*)/);

    return (
      <Highlight
        {...defaultProps}
        code={props.children.props.children.trim()}
        language={
          matches && matches.groups && matches.groups.lang
            ? matches.groups.lang
            : ''
        }
        theme={theme}>
        {({
          className,
          style,
          tokens,
          getLineProps,
          getTokenProps,
        }) => (
          <pre className={className} style={style}>
            {tokens.map((line, i) => (
              <div {...getLineProps({ line, key: i })}>
                {line.map((token, key) => (
                  <span {...getTokenProps({ token, key })} />
                ))}
              </div>
            ))}
          </pre>
        )}
      </Highlight>
    );
  },
};

export const wrapRootElement = ({ element }) => (
  <MDXProvider components={components}>{element}</MDXProvider>
);

Ok, now time to abstract this out into its own component so your root-wrapper.js isn’t so crowded.

好的,现在是时候将其抽象为自己的组件了,这样您的root-wrapper.js就不会那么拥挤了。

Make a Code.js component, and move the code from root-wrapper.js into there:

创建一个Code.js组件,然后将代码从root-wrapper.js移到其中:

touch src/components/Code.js

Remember this?

记住这一点?

Cool, cool! Now you want to replace the pasted in code example with the props of the child component of the pre component. You can do that with props.children.props.children.trim() 🙃.

酷,酷! 现在,您要用pre组件的child组件的props替换粘贴在代码示例中的代码。 您可以使用props.children.props.children.trim() that做到这一点。

If that ☝ makes no real amount of sense for you (I’ve had to read it many, many times myself), don’t worry: now you’re going to dig into that a bit more for the creation of the code block component.

如果那个for对您没有任何意义(我自己必须读过很多遍),请不要担心:现在,您将对代码块的创建进行更多的研究。零件。

So, for now in the components you’re adding into the MDXProvider, take a look at the props coming into the pre element.

因此,现在,在您要添加到MDXProvider中的components ,看看pre元素中的props

Comment out the code you added earlier and add in a console.log:

注释掉您之前添加的代码,并添加到console.log

pre: props => {
  console.log('=====================');
  console.log(props);
  console.log('=====================');
  return <pre />;
};

Now if you pop open the developer tools of your browser you can see the output.

现在,如果您打开浏览器的开发人员工具,则可以看到输出。

{children: {…}}
  children:
    $$typeof: Symbol(react.element)
    key: null
    props: {parentName: "pre", className: "language-jsx", originalType: "code", mdxType: "code", children: "import React from 'react'↵↵const Dump = props => (…  </pre>↵    ))}↵  </div>↵)↵↵export default Dump↵"}
    ref: null
    type: ƒ (re....

If you drill into the props of that output you can see the children of those props. If you take a look at the contents of that you will see that it is the code string for your code block. This is what you’re going to be passing into the Code component you’re about to create. Other properties to note here are the className and mdxType.

如果您深入了解该输出的道具,则可以看到这些道具的children 。 如果您看一下其中的内容,将会看到它是您代码块的代码字符串。 这就是将要传递到要创建的Code组件中的内容。 这里要注意的其他属性是classNamemdxType

So, take the code you used earlier for Highlight, everything inside and including the return statement, and paste it into the Code.js module you created earlier.

因此,采用您先前用于Highlight的代码(包括return语句在内的所有内容),并将其粘贴到您先前创建的Code.js模块中。

Highlight requires several props:

Highlight需要几个道具:

<Highlight
  {...defaultProps}
  code={codeString}
  language={language}
  theme={theme}
>

The Code module should look something like this now:

Code模块现在应该看起来像这样:

import Highlight, { defaultProps } from 'prism-react-renderer';
import theme from 'prism-react-renderer/themes/nightOwl';
import React from 'react';

const Code = ({ codeString, language }) => {
  return (
    <Highlight
      {...defaultProps}
      code={codeString}
      language={language}
      theme={theme}>
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) => (
        <pre className={className} style={style}>
          {tokens.map((line, i) => (
            <div {...getLineProps({ line, key: i })}>
              {line.map((token, key) => (
                <span {...getTokenProps({ token, key })} />
              ))}
            </div>
          ))}
        </pre>
      )}
    </Highlight>
  );
};

export default Code;

Back to the root-wrapper where you’re going to pass the props needed to the Code component.

返回到root-wrapper ,您将在其中将所需的props传递给Code组件。

The first check you’re going to do is if the mdxType is code then you can get the additional props you need to pass to your Code component.

您要做的第一项检查是mdxType是否为code那么您可以获取传递给Code组件所需的其他道具。

You’re going to get defaultProps and the theme from prism-react-renderer so all that’s needed is the code and language.

您将从prism-react-renderer获得defaultPropstheme ,因此所需的只是codelanguage

The codeString you can get from the props, and the children by destructuring from the props being passed into the pre element. The language can either be the tag assigned to the meta property of the backticks, like js, jsx or equally empty. So you check for that with some JavaScript and also remove the language- prefix, then pass in the elements {...props}:

codeString可以从得到props ,和children从解构props被传递到pre元素。 language可以是分配给反引号的meta属性的标签,例如jsjsx或同样为空。 因此,您可以使用一些JavaScript进行检查,并删除language-前缀,然后传入元素{...props}

pre: ({ children: { props } }) => {
  if (props.mdxType === 'code') {
    return (
      <Code
        codeString={props.children.trim()}
        language={
          props.className && props.className.replace('language-', '')
        }
        {...props}
      />
    );
  }
};

Ok, now you’re back to where you were before abstracting out the Highlight component to it’s own module. Add some additional styles with styled-components and replace the pre with a styled Pre. You can also add in some line numbers with a styled span and style that as well.

好的,现在您回到了原来的位置,然后将Highlight组件抽象到它自己的模块中。 使用styled-components添加一些其他样式,并将pre替换为样式Pre 。 您还可以添加一些带有样式化跨度和样式的行号。

import Highlight, { defaultProps } from 'prism-react-renderer';
import theme from 'prism-react-renderer/themes/nightOwl';
import React from 'react';
import styled from 'styled-components';

export const Pre = styled.pre`
  text-align: left;
  margin: 1em 0;
  padding: 0.5em;
  overflow-x: auto;
  border-radius: 3px;

  & .token-line {
    line-height: 1.3em;
    height: 1.3em;
  }
  font-family: 'Courier New', Courier, monospace;
`;

export const LineNo = styled.span`
  display: inline-block;
  width: 2em;
  user-select: none;
  opacity: 0.3;
`;

const Code = ({ codeString, language, ...props }) => {
  return (
    <Highlight
      {...defaultProps}
      code={codeString}
      language={language}
      theme={theme}>
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) => (
        <Pre className={className} style={style}>
          {tokens.map((line, i) => (
            <div {...getLineProps({ line, key: i })}>
              <LineNo>{i + 1}</LineNo>
              {line.map((token, key) => (
                <span {...getTokenProps({ token, key })} />
              ))}
            </div>
          ))}
        </Pre>
      )}
    </Highlight>
  );
};

export default Code;

将代码复制到剪贴板 (Copy code to clipboard)

What if you had some way of getting that props code string into the clipboard?

如果您可以通过某种方式将道具代码字符串放入剪贴板,该怎么办?

I had a look around and found the majority of the components available for this sort of thing expected an input until this in the Gatsby source code. Which is creating the input for you 👌

我周围一看,发现大多数可用这样的事情组件的预期,直到输入这个在盖茨比的源代码。 哪个在为您创建输入👌

So, create a utils directory and the copy-to-clipboard.js file and add in the code from the Gatsby source code.

因此,创建一个utils目录和copy-to-clipboard.js文件,并添加Gatsby源代码中的代码。

mkdir src/utils
touch src/utils/copy-to-clipboard.js
// https://github.com/gatsbyjs/gatsby/blob/master/www/src/utils/copy-to-clipboard.js

export const copyToClipboard = str => {
  const clipboard = window.navigator.clipboard;
  /*
   * fallback to older browsers (including Safari)
   * if clipboard API not supported
   */
  if (!clipboard || typeof clipboard.writeText !== `function`) {
    const textarea = document.createElement(`textarea`);
    textarea.value = str;
    textarea.setAttribute(`readonly`, true);
    textarea.setAttribute(`contenteditable`, true);
    textarea.style.position = `absolute`;
    textarea.style.left = `-9999px`;
    document.body.appendChild(textarea);
    textarea.select();
    const range = document.createRange();
    const sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
    textarea.setSelectionRange(0, textarea.value.length);
    document.execCommand(`copy`);
    document.body.removeChild(textarea);

    return Promise.resolve(true);
  }

  return clipboard.writeText(str);
};

Now you’re going to want a way to trigger copying the code to the clipboard.

现在,您将需要一种触发将代码复制到剪贴板的方法。

Let's create a styled button. But first add a position: relative; to the Pre component which will let us position the styled button:

让我们创建一个样式按钮。 但首先要添加一个position: relative;Pre组件,这将让我们定位样式按钮:

const CopyCode = styled.button`
  position: absolute;
  right: 0.25rem;
  border: 0;
  border-radius: 3px;
  margin: 0.25em;
  opacity: 0.3;
  &:hover {
    opacity: 1;
  }
`;

And now you need to use the copyToClipboard function in the onClick of the button:

现在,您需要在按钮的onClick中使用copyToClipboard函数:

import Highlight, { defaultProps } from 'prism-react-renderer';
import theme from 'prism-react-renderer/themes/nightOwl';
import React from 'react';
import styled from 'styled-components';
import { copyToClipboard } from '../utils/copy-to-clipboard';

export const Pre = styled.pre`
  text-align: left;
  margin: 1rem 0;
  padding: 0.5rem;
  overflow-x: auto;
  border-radius: 3px;

  & .token-line {
    line-height: 1.3rem;
    height: 1.3rem;
  }
  font-family: 'Courier New', Courier, monospace;
  position: relative;
`;

export const LineNo = styled.span`
  display: inline-block;
  width: 2rem;
  user-select: none;
  opacity: 0.3;
`;

const CopyCode = styled.button`
  position: absolute;
  right: 0.25rem;
  border: 0;
  border-radius: 3px;
  margin: 0.25em;
  opacity: 0.3;
  &:hover {
    opacity: 1;
  }
`;

const Code = ({ codeString, language }) => {
  const handleClick = () => {
    copyToClipboard(codeString);
  };

  return (
    <Highlight
      {...defaultProps}
      code={codeString}
      language={language}
      theme={theme}>
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) => (
        <Pre className={className} style={style}>
          <CopyCode onClick={handleClick}>Copy</CopyCode>
          {tokens.map((line, i) => (
            <div {...getLineProps({ line, key: i })}>
              <LineNo>{i + 1}</LineNo>
              {line.map((token, key) => (
                <span {...getTokenProps({ token, key })} />
              ))}
            </div>
          ))}
        </Pre>
      )}
    </Highlight>
  );
};

export default Code;

实时React (React live)

So with React Live you need to add two snippets to your Code.js component.

因此,使用React Live,您需要向Code.js组件添加两个片段。

You’re going to import the components:

您将要导入组件:

import {
  LiveEditor,
  LiveError,
  LivePreview,
  LiveProvider,
} from 'react-live';

Then you’re going to check if react-live has been added to the language tag on your mdx file via the props:

然后,您将检查是否通过props将react-live添加到了mdx文件的language标签中:

if (props['react-live']) {
  return (
    <LiveProvider code={codeString} noInline={true} theme={theme}>
      <LiveEditor />
      <LiveError />
      <LivePreview />
    </LiveProvider>
  );
}

Here’s the full component:

这是完整的组件:

import Highlight, { defaultProps } from 'prism-react-renderer';
import theme from 'prism-react-renderer/themes/nightOwl';
import React from 'react';
import {
  LiveEditor,
  LiveError,
  LivePreview,
  LiveProvider,
} from 'react-live';
import styled from 'styled-components';
import { copyToClipboard } from '../../utils/copy-to-clipboard';

const Pre = styled.pre`
  position: relative;
  text-align: left;
  margin: 1em 0;
  padding: 0.5em;
  overflow-x: auto;
  border-radius: 3px;

  & .token-lline {
    line-height: 1.3em;
    height: 1.3em;
  }
  font-family: 'Courier New', Courier, monospace;
`;

const LineNo = styled.span`
  display: inline-block;
  width: 2em;
  user-select: none;
  opacity: 0.3;
`;

const CopyCode = styled.button`
  position: absolute;
  right: 0.25rem;
  border: 0;
  border-radius: 3px;
  margin: 0.25em;
  opacity: 0.3;
  &:hover {
    opacity: 1;
  }
`;

export const Code = ({ codeString, language, ...props }) => {
  if (props['react-live']) {
    return (
      <LiveProvider code={codeString} noInline={true} theme={theme}>
        <LiveEditor />
        <LiveError />
        <LivePreview />
      </LiveProvider>
    );
  }

  const handleClick = () => {
    copyToClipboard(codeString);
  };

  return (
    <Highlight
      {...defaultProps}
      code={codeString}
      language={language}
      theme={theme}>
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) => (
        <Pre className={className} style={style}>
          <CopyCode onClick={handleClick}>Copy</CopyCode>
          {tokens.map((line, i) => (
            <div {...getLineProps({ line, key: i })}>
              <LineNo>{i + 1}</LineNo>
              {line.map((token, key) => (
                <span {...getTokenProps({ token, key })} />
              ))}
            </div>
          ))}
        </Pre>
      )}
    </Highlight>
  );
};

To test this, add react-live next to the language on your Dump component, so you have added to the blog post you made:

要对此进行测试,请在Dump组件上的语言旁边添加react-live ,因此您已将其添加到您Dump的博客文章中:

```jsx react-live

Now you can edit the code directly. Try changing a few things like this:

现在,您可以直接编辑代码。 尝试更改以下内容:

const Dump = props => (
  <div
    style={{
      fontSize: 20,
      border: '1px solid #efefef',
      padding: 10,
      background: 'white',
    }}>
    {Object.entries(props).map(([key, val]) => (
      <pre key={key}>
        <strong style={{ color: 'white', background: 'red' }}>
          {key} 💩
        </strong>
        {JSON.stringify(val, '', ' ')}
      </pre>
    ))}
  </div>
);

render(<Dump props={['One', 'Two', 'Three', 'Four']} />);

封面图片 (Cover Image)

Now to add a cover image to go with each post, you’ll need to install a couple of packages to manage images in Gatsby.

现在,要在每个帖子中添加封面图像,您需要安装几个软件包来管理Gatsby中的图像。

install:

安装:

yarn add gatsby-transformer-sharp gatsby-plugin-sharp gatsby-remark-images gatsby-image

Now you should config gatsby-config.js to include the newly added packages. Remember to add gatsby-remark-images to gatsby-plugin-mdx as both a gatsbyRemarkPlugins option and as a plugins option.

现在,您应该配置gatsby-config.js以包括新添加的软件包。 记住要同时将gatsby-remark-images添加到gatsby-plugin-mdx ,既作为gatsbyRemarkPlugins选项,又作为plugins选项。

config:

配置:

module.exports = {
  siteMetadata: siteMetadata,
  plugins: [
    `gatsby-plugin-styled-components`,
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-mdx`,
      options: {
        extensions: [`.mdx`, `.md`],
        gatsbyRemarkPlugins: [
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 590,
            },
          },
        ],
        plugins: [
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 590,
            },
          },
        ],
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: { path: `${__dirname}/posts`, name: `posts` },
    },
  ],
};

Add image to index query in src/pages.index.js:

将图像添加到src/pages.index.js索引查询中:

cover {
  publicURL
  childImageSharp {
    sizes(
      maxWidth: 2000
      traceSVG: { color: "#639" }
    ) {
      ...GatsbyImageSharpSizes_tracedSVG
    }
  }
}

Fix up the date in the query too:

也确定查询中的日期:

date(formatString: "YYYY MMMM Do")

This will show the date as full year, full month and the day as a ‘st’, ‘nd’, ‘rd’ and ‘th’. So if today’s date were 1970/01/01 it would read 1970 January 1st.

这会将日期显示为全年,整月,并将日期显示为“ st”,“ nd”,“ rd”和“ th”。 因此,如果今天的日期是1970/01/01,它将显示为1970年1月1日。

Add gatsby-image use that in a styled component:

gatsby-image添加到样式化组件中:

const Image = styled(Img)`
  border-radius: 5px;
`;

Add some JavaScript to determine if there’s anything to render:

添加一些JavaScript,以确定是否有任何可呈现的内容:

{
  !!frontmatter.cover ? (
    <Image sizes={frontmatter.cover.childImageSharp.sizes} />
  ) : null;
}

Here’s what the full module should look like now:

这是完整模块现在的样子:

import { Link } from 'gatsby';
import Img from 'gatsby-image';
import React from 'react';
import styled from 'styled-components';
import { Layout } from '../components/Layout';

const IndexWrapper = styled.main``;

const PostWrapper = styled.div``;

const Image = styled(Img)`
  border-radius: 5px;
`;

export default ({ data }) => {
  return (
    <Layout>
      <IndexWrapper>
        {/* <Dump data={data}></Dump> */}
        {data.allMdx.nodes.map(
          ({ id, excerpt, frontmatter, fields }) => (
            <PostWrapper key={id}>
              <Link to={fields.slug}>
                {!!frontmatter.cover ? (
                  <Image
                    sizes={frontmatter.cover.childImageSharp.sizes}
                  />
                ) : null}
                <h1>{frontmatter.title}</h1>
                <p>{frontmatter.date}</p>
                <p>{excerpt}</p>
              </Link>
            </PostWrapper>
          )
        )}
      </IndexWrapper>
    </Layout>
  );
};

export const query = graphql`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date(formatString: "YYYY MMMM Do")
          cover {
            publicURL
            childImageSharp {
              sizes(maxWidth: 2000, traceSVG: { color: "#639" }) {
                ...GatsbyImageSharpSizes_tracedSVG
              }
            }
          }
        }
        fields {
          slug
        }
      }
    }
  }
`;

Additional resources:

其他资源:

使用React Helmet创建SEO组件 (Creating an SEO component with React Helmet)

There’s a Gatsby github PR on seo with some great notes from Andrew Welch on SEO and a link to a presentation he did back in 2017.

seo上有一个盖茨比(Gatsby) github公关 ,其中安德鲁·韦尔奇(Andrew Welch)关于SEO的一些精彩笔记以及他在2017年所做演讲的链接。

Crafting Modern SEO with Andrew Welch:

与Andrew Welch一起制作现代SEO:

Crafting Modern SEO with Andrew Welch:

与Andrew Welch一起制作现代SEO:

In the following comments of that PR, Gatsby’s LekoArts details his own implementation which I have implemented as a React component. You’re going to be configuring that now in this how-to.

在该PR的以下评论中,Gatsby的LekoArts详细介绍了他自己的实现 ,我已将其实现为React组件 。 您现在将在此操作方法中进行配置。

First up, install and configure gatsby-plugin-react-helmet. This is used for server rendering data added with React Helmet.

首先,安装并配置gatsby-plugin-react-helmet 。 这用于随React Helmet添加的服务器渲染数据。

yarn add gatsby-plugin-react-helmet

You’ll need to add the plugin to your gatsby-config.js. If you haven’t done so already now is a good time to also configure the gatsby-plugin-styled-components as well.

您需要将插件添加到gatsby-config.js 。 如果您现在还没有这样做,那么现在也是同时配置gatsby-plugin-styled-components的好时机。

为主页配置SEO组件 (Configure SEO Component for Homepage)

To visualise the data you’re going to need to get into the SEO component use the Dump component to begin with to validate the data.

要形象化数据,您需要进入SEO组件,请使用Dump组件开始验证数据。

The majority of the information needed for src/pages/index.js can be first added to the gatsby-config.js, siteMetadata object then queried with the useSiteMetadata hook. Some of the data added here can then be used in src/templates/blogPostTemplate.js – more on that in the next section.

src/pages/index.js所需的大多数信息可以首先添加到gatsby-config.jssiteMetadata对象中,然后使用useSiteMetadata钩子进行查询。 然后,可以在src/templates/blogPostTemplate.js使用此处添加的某些数据- src/templates/blogPostTemplate.js更多说明。

For now add the following:

现在添加以下内容:

const siteMetadata = {
  title: `The Localhost Blog`,
  description: `This is my coding blog where I write about my coding journey.`,
  image: `/default-site-image.jpg`,
  siteUrl: `https://thelocalhost.blog`,
  siteLanguage: `en-GB`,
  siteLocale: `en_gb`,
  twitterUsername: `@spences10`,
  authorName: `Scott Spence`,
}

module.exports = {
  siteMetadata: siteMetadata,
  plugins: [
    ...

You don’t have to abstract out the siteMetadata into its own component here. It’s only a suggestion on how to manage it.

您不必在这里将siteMetadata抽象为它自己的组件。 这只是有关如何进行管理的建议。

The image is going to be the default image for your site. You should create a static folder at the root of the project and add in an image you want to be shown when the homepage of your site is shared on social media.

image将成为您网站的默认图像。 您应该在项目的根目录创建一个static文件夹,并添加要在社交媒体上共享您的网站主页时显示的图像。

For siteUrl at this stage it doesn’t necessarily have to be valid. You can add a dummy url for now and change it later.

对于现阶段的siteUrl ,不一定必须有效。 您可以暂时添加一个虚拟网址,以后再更改。

The siteLanguage is your language of choice for the site. Take a look at w3 language tags for more info.

siteLanguage是您选择的网站语言。 查看w3语言标签以获取更多信息。

Facebook OpenGraph is the only place the siteLocale is used and it is different from language tags.

Facebook的OpenGraph只是地方siteLocale被使用,它是从语言标记不同。

Add your twitterUsername and your authorName.

添加您的twitterUsernameauthorName

Update the useSiteMetadata hook now to reflect the newly added properties:

立即更新useSiteMetadata挂钩以反映新添加的属性:

import { graphql, useStaticQuery } from 'gatsby';

export const useSiteMetadata = () => {
  const { site } = useStaticQuery(
    graphql`
      query SITE_METADATA_QUERY {
        site {
          siteMetadata {
            description
            title
            image
            siteUrl
            siteLanguage
            siteLocale
            twitterUsername
            authorName
          }
        }
      }
    `
  );
  return site.siteMetadata;
};

Begin with importing the Dump component in src/pages/index.js then plug in the props as they are detailed in the docs of the react-seo-component.

首先将Dump组件导入src/pages/index.js然后插入props(在react-seo-component的文档中进行了详细介绍)。

import Dump from '../components/Dump'
import { useSiteMetadata } from '../hooks/useSiteMetadata'

export default ({ data }) => {
  const {
    description,
    title,
    image,
    siteUrl,
    siteLanguage,
    siteLocale,
    twitterUsername,
  } = useSiteMetadata()
  return (
    <Layout>
      <Dump
        title={title}
        description={description}
        image={`${siteUrl}${image}`}
        pathname={siteUrl}
        siteLanguage={siteLanguage}
        siteLocale={siteLocale}
        twitterUsername={twitterUsername}
      />
      <IndexWrapper>
        {data.allMdx.nodes.map(
          ...

Check that all the props are displaying valid values. Then you can swap out the Dump component with the SEO component.

检查所有道具是否显示有效值。 然后,您可以将SEO组件替换为Dump组件。

The complete src/pages/index.js should look like this now:

完整的src/pages/index.js现在应该像这样:

import { graphql, Link } from 'gatsby';
import Img from 'gatsby-image';
import React from 'react';
import SEO from 'react-seo-component';
import styled from 'styled-components';
import { Layout } from '../components/Layout';
import { useSiteMetadata } from '../hooks/useSiteMetadata';

const IndexWrapper = styled.main``;

const PostWrapper = styled.div``;

const Image = styled(Img)`
  border-radius: 5px;
`;

export default ({ data }) => {
  const {
    description,
    title,
    image,
    siteUrl,
    siteLanguage,
    siteLocale,
    twitterUsername,
  } = useSiteMetadata();
  return (
    <Layout>
      <SEO
        title={title}
        description={description || `nothin’`}
        image={`${siteUrl}${image}`}
        pathname={siteUrl}
        siteLanguage={siteLanguage}
        siteLocale={siteLocale}
        twitterUsername={twitterUsername}
      />
      <IndexWrapper>
        {/* <Dump data={data}></Dump> */}
        {data.allMdx.nodes.map(
          ({ id, excerpt, frontmatter, fields }) => (
            <PostWrapper key={id}>
              <Link to={fields.slug}>
                {!!frontmatter.cover ? (
                  <Image
                    sizes={frontmatter.cover.childImageSharp.sizes}
                  />
                ) : null}
                <h1>{frontmatter.title}</h1>
                <p>{frontmatter.date}</p>
                <p>{excerpt}</p>
              </Link>
            </PostWrapper>
          )
        )}
      </IndexWrapper>
    </Layout>
  );
};

export const query = graphql`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date(formatString: "YYYY MMMM Do")
          cover {
            publicURL
            childImageSharp {
              sizes(maxWidth: 2000, traceSVG: { color: "#639" }) {
                ...GatsbyImageSharpSizes_tracedSVG
              }
            }
          }
        }
        fields {
          slug
        }
      }
    }
  }
`;

为博客文章配置SEO组件 (Configure SEO Component for Blog Posts)

This will be the same approach as with the homepage. Import the Dump component and validate the props before swapping out the Dump component with the SEO component.

这将与主页使用相同的方法。 导入Dump组件并验证道具,然后再将Dump组件替换为SEO组件。

import Dump from '../components/Dump'
import { useSiteMetadata } from '../hooks/useSiteMetadata'

export default ({ data, pageContext }) => {
  const {
    image,
    siteUrl,
    siteLanguage,
    siteLocale,
    twitterUsername,
    authorName,
  } = useSiteMetadata()
  const { frontmatter, body, fields, excerpt } = data.mdx
  const { title, date, cover } = frontmatter
  const { previous, next } = pageContext
  return (
    <Layout>
      <Dump
        title={title}
        description={excerpt}
        image={
          cover === null
            ? `${siteUrl}${image}`
            : `${siteUrl}${cover.publicURL}`
        }
        pathname={`${siteUrl}${fields.slug}`}
        siteLanguage={siteLanguage}
        siteLocale={siteLocale}
        twitterUsername={twitterUsername}
        author={authorName}
        article={true}
        publishedDate={date}
        modifiedDate={new Date(Date.now()).toISOString()}
      />
      <h1>{frontmatter.title}</h1>
      ...

Add fields.slug, excerpt and cover.publicURL to the PostsBySlug query and destructure them from data.mdx and frontmatter, respectively.

添加fields.slugexcerptcover.publicURLPostsBySlug查询,并从解构他们data.mdxfrontmatter分别。

For the image you’ll need to do some logic as to whether the cover exists and default to the default site image if it doesn’t.

对于图像,您需要确定cover是否存在,如果没有,默认为默认站点图像。

The complete src/templates/blogPostTemplate.js should look like this now:

现在,完整的src/templates/blogPostTemplate.js应该看起来像这样:

import { graphql, Link } from 'gatsby';
import { MDXRenderer } from 'gatsby-plugin-mdx';
import React from 'react';
import SEO from 'react-seo-component';
import { Layout } from '../components/Layout';
import { useSiteMetadata } from '../hooks/useSiteMetadata';

export default ({ data, pageContext }) => {
  const {
    image,
    siteUrl,
    siteLanguage,
    siteLocale,
    twitterUsername,
    authorName,
  } = useSiteMetadata();
  const { frontmatter, body, fields, excerpt } = data.mdx;
  const { title, date, cover } = frontmatter;
  const { previous, next } = pageContext;
  return (
    <Layout>
      <SEO
        title={title}
        description={excerpt}
        image={
          cover === null
            ? `${siteUrl}${image}`
            : `${siteUrl}${cover.publicURL}`
        }
        pathname={`${siteUrl}${fields.slug}`}
        siteLanguage={siteLanguage}
        siteLocale={siteLocale}
        twitterUsername={twitterUsername}
        author={authorName}
        article={true}
        publishedDate={date}
        modifiedDate={new Date(Date.now()).toISOString()}
      />
      <h1>{frontmatter.title}</h1>
      <p>{frontmatter.date}</p>
      <MDXRenderer>{body}</MDXRenderer>
      {previous === false ? null : (
        <>
          {previous && (
            <Link to={previous.fields.slug}>
              <p>{previous.frontmatter.title}</p>
            </Link>
          )}
        </>
      )}
      {next === false ? null : (
        <>
          {next && (
            <Link to={next.fields.slug}>
              <p>{next.frontmatter.title}</p>
            </Link>
          )}
        </>
      )}
    </Layout>
  );
};

export const query = graphql`
  query PostBySlug($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      frontmatter {
        title
        date(formatString: "YYYY MMMM Do")
        cover {
          publicURL
        }
      }
      body
      excerpt
      fields {
        slug
      }
    }
  }
`;

建立网站并验证元标记 (Build Site and Validate Meta Tags)

Add in the build script to package.json and also a script for serving the built site locally.

将构建脚本添加到package.json并将脚本添加到本地服务于构建站点。

"scripts": {
  "dev": "gatsby develop -p 9988 -o",
  "build": "gatsby build",
  "serve": "gatsby serve -p 9500 -o"
},

Now it’s time to run:

现在该运行了:

yarn build && yarn serve

This will build the site and open a browser tab so you can see the site as it will appear when it is on the internet. Validate meta tags have been added to the build by selecting “View page source” (Crtl+u in Windows and Linux) on the page. You can do a Ctrl+f to find them.

这将构建站点并打开浏览器选项卡,以便您可以看到该站点在互联网上时的样子。 通过选择页面上的“查看页面源代码”(在Windows和Linux中为Crtl + u),将验证元标记添加到了版本中。 您可以执行Ctrl + f来找到它们。

将项目添加到GitHub (Adding the Project to GitHub)

Add your code to GitHub by either selecting the plus (+) icon next to your avatar on GitHub or by going directly to https://github.com/new

通过选择GitHub头像旁边的加号(+)图标或直接转到https://github.com/new ,将代码添加到GitHub

Name your repository and click create repository. Then you will be given the instructions to link your local code to the repository you created via the command line.

命名您的存储库,然后单击创建存储库。 然后,您将获得将本地代码链接到通过命令行创建的存储库的说明。

How you authenticate with GitHub will depend on what the command looks like.

您如何通过GitHub进行身份验证将取决于命令的外观。

Some good resources for authenticating with GitHub via SSH are Kent Dodds Egghead.io video and also a how-to on CheatSheets.xyz.

有一些很好的资源,通过SSH与GitHub的认证是肯特兹Egghead.io视频 ,也是一个如何做的CheatSheets.xyz

部署到Netlify (Deploy to Netlify)

To deploy your site to Netlify, if you haven’t done so already, you’ll need to add the GitHub integration to your GitHub profile. If you got to app.netlify.com the wizard will walk you through the process.

要将站点部署到Netlify,如果尚未部署,则需要将GitHub集成添加到GitHub配置文件中。 如果您访问了app.netlify.com ,向导将引导您完成该过程。

From here you can add your built site’s public folder drag ‘n drop style directly to the Netlify global CDNs.

从这里你可以添加你建网站的public文件夹拖到“东经直接拖放风格的Netlify全球的CDN。

You, however, are going to load your site via the Netlify CLI! In your terminal, if you haven’t already got the CLI installed, run:

但是,您将通过Netlify CLI加载站点! 在终端中,如果尚未安装CLI,请运行:

yarn global add netlify-cli

Then once the CLI is installed:

然后在安装CLI后:

# authenticate via the CLI
netlify login
# initialise the site
netlify init

Enter the details for your team: the site name is optional, the build command will be yarn build, and directory to deploy is public.

输入您的团队的详细信息:站点名称是可选的,build命令将是yarn build ,要部署的目录是public

You will be prompted to commit the changes and push them to GitHub (with git push). Once you have done that your site will be published and ready for all to see!

系统将提示您提交更改并将其推送到GitHub(使用git push )。 完成此操作后,您的站点将被发布并可供所有人查看!

使用Heymeta验证元数据 (Validate Metadata with Heymeta)

Last up is validating the metadata for the OpenGraph fields. To do that you’ll need to make sure that the siteUrl reflects what you have in your Netlify dashboard.

最后是验证OpenGraph字段的元数据。 要做到这一点,你需要确保的siteUrl反映了你在你的Netlify仪表板。

If you needed to change the url you’ll need to commit and push the changes to GitHub again.

如果需要更改url,则需要提交并再次将更改推送到GitHub。

Once your site is built with a valid url you can then test the homepage and a blog page for the correct meta tags with heymeta.com.

使用有效的URL构建网站之后,您可以使用heymeta.com测试主页和博客页面的正确元标记。

OpenGraph checking tools:

OpenGraph检查工具:

Additional resources:

其他资源:

Example Code for this Blog can be found here:

示例代码这个博客可以发现在这里

Or here.

还是这里

感谢您阅读🙏 (Thanks for reading 🙏)

That’s all folks! If there is anything I have missed, or if there is a better way to do something, then please let me know.

那是所有人! 如果我错过了任何事情,或者有更好的方法做某事,请告诉我。

Follow me on Twitter or Ask Me Anything on GitHub.

Twitter上关注我,或在GitHub上询问我

You can read other articles like this on my digital garden.

您可以在我的数字花园中阅读其他类似文章。

翻译自: https://www.freecodecamp.org/news/build-a-developer-blog-from-scratch-with-gatsby-and-mdx/

gatsby

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值