如何用Gatsby和MDX建立一个开发者博客

451 阅读8分钟

你可以很容易地将你的想法发布到Dev.to、Hashnode或Medium等网站上,但最理想的是完全控制你自己的内容。现在有一个不断增长的工具清单,用于建立你自己的网站和控制你自己的内容。在这个广泛的教程中,我将介绍如何使用Gatsby使你的内容大放异彩,并在这样一个生态系统中得到更多的钟声和口哨。

我最初使用Jekyll来发布我的博客,但后来改用Gatsby,使用Lumen模板。我从0版本开始使用Gatsby,大约在2017年5月。

我将从一个Hello, World! Gatsby项目,通过代码语法高亮和主题切换来实现这种黑暗模式的好处,成为一个编码博客。

Gatsby有一个丰富的插件、启动器和主题的生态系统,可以让你快速启动和运行,但我想采取渐进式披露的方法来介绍Gatsby,重点是Gatsby项目如何运作的基本原理。

为什么是Gatsby?

Gatsby是一个静态网站生成器,所以当页面被请求时,没有动态生成的页面。Gatsby网站的构建输出可以托管在CDN上,使其在全球范围内可用,并具有超级可扩展性。

Gatsby可以使用Markdown文件来创建网站项目中的页面。Gatsby会将Markdown文件读入Gatsby文件系统,并将Markdown转化为HTML,然后在构建网站时创建静态页面。

最终的结果是一个超级快速的网站,在请求页面时几乎没有延迟。

马克笔和MDX

自2016年以来,我一直在用Markdown记录我的开发历程。Markdown提供了一种在纯文本文件中实现简单编辑的方式,可以转换为HTML。

MDX(或Markdown JSX)是一种工具,可以让你在Markdown文档中编写JSX,有点像这样。

import { RainbowText } from './components/rainbow';
## A Markdown Heading
<RainbowText>Wheeeeeeee</RainbowText>

Gatsby是迄今为止我使用过的最好的Markdown和MDX的框架,因为在你的文章上使用frontmatter,不需要特殊的符号。

我需要什么?

如果你打算跟着做,你需要一些东西。

  • 一个基本的网络开发设置。Node、终端(bash、zsh或fish)。
  • 一个文本编辑器
  • 对React的基本了解

如果你没有这些东西,StackBlitz和GitHubCodespaces都有,你可以在那里创建一个空的GitHub仓库,然后从那里开始使用开发环境。

在下面的例子中,我将使用VS Code作为我的文本编辑器,Yarn作为我的首选软件包管理器。如果你喜欢npm,那也很酷。👍

你也可以在GitHub上找到本教程的完整代码

好了,是时候开始了!

你好,世界!

现在是时候启动一个Gatsby项目了。我将从命令行开始做大部分的工作。

# create the project directory
mkdir my-gatsby-blog
# change into the directory
cd my-gatsby-blog
# initialise a package.json file
yarn init -y
# initialise the git repo
git init

酷,现在,在进行其他工作之前,我需要在安装任何npm模块之前添加一个.gitignore 文件。

# create .gitignore file in my directory
touch .gitignore
# add ignore contents with echo
echo "# Project dependencies
.cache
node_modules

# Build directory
public

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

现在我可以安装所有我需要的npm好东西了,而不需要VS Code Git对我大喊大叫,说有太多的活动变化。现在,让我们安装一些依赖项,以启动和运行Gatsby。

yarn add gatsby react react-dom
# -p is to create parent directories too if needed
mkdir -p src/pages
# create the index (home) page
touch src/pages/index.js

接下来,我们将为该项目添加第一个React组件(很多)。我将在我创建的index.js 文件中添加以下内容。

import React from "react";

export default function IndexPage() {
  return <h1>Hello, World!</h1>;
}

现在我已经准备好从命令行运行Gatsbydevelop 命令。

# if you're using npm 👇
# $(npm bin)/gatsby develop
yarn gatsby develop

这将启动Gatsby开发服务器,并说我的项目可以在8000端口(Gatsby默认端口)的浏览器中查看。URL是http://localhost:8000/。

直接从命令行界面(CLI)使用Gatsby二进制命令是完全可行的,但大多数人将把可用的命令添加到package.json 文件中的scripts 部分,像这样。

"scripts": {
  "build": "gatsby build",
  "dev": "gatsby develop",
  "serve": "gatsby serve",
  "clean": "gatsby clean"
},

作为一个额外的奖励,这里有一些额外的东西可以添加到Gatsby脚本中。

如果我们不想每次都在同一个端口上运行项目,可以用-p 标志来改变它,并在后面指定一个端口。例如,gatsby develop -p 8945

如果我们想在项目完成后打开浏览器标签,我们可以在脚本中加入-o

我也会对serve 脚本做同样的处理,这样我就知道当我建立一个项目时,它的端口和开发端口是不同的。

"scripts": {
  "build": "gatsby build",
  "dev": "gatsby develop -p 8945 -o",
  "serve": "gatsby serve -p 9854 -o",
  "clean": "gatsby clean"
},

就这样,强制性的 "你好,世界!"的欢迎仪式完成了,我可以继续写这篇文章的其他部分了🤓

最后,我将提交我到目前为止所做的修改。

# add everything for committing
git add .
# commit to repo
git commit -m 'init project'

博客的内容

好了,现在这个项目没有什么大的进展,所以首先我将再次从命令行中添加一些内容。

# this creates the folders in the root of the project
mkdir -p content/2021/03/{06/hello-world,07/second-post,08/third-post}
# create individual files
touch content/2021/03/06/hello-world/index.mdx
touch content/2021/03/07/second-post/index.mdx
touch content/2021/03/08/third-post/index.mdx

我将在我所做的整个例子中使用这些内容。

你会注意到文件的扩展名是.mdx 。这是一个MDX文件。

前面的内容

在我为博客添加一些内容之前,我需要谈一谈正面内容。

Front matter是一种存储文件信息的方式,当Gatsby从文件中构建页面时,可以使用这些信息。现在,我将添加文章的titledate 。我还将为它们添加一些内容。这是我们的第一个帖子。

---
title: Hello World - from mdx!
date: 2021-03-06
---

My first post!!

## h2 Heading

Some meaningful prose

### h3 Heading

Some other meaningful prose

这是我们的第二篇帖子。

---
title: Second Post!
date: 2021-03-07
---

This is my second post!

第三篇帖子。

---
title: Third Post!
date: 2021-03-08
---

This is my third post!

> with a block quote!

And a code block:

```
const wheeeeee = true;
```

这就是目前的帖子,因为这些帖子还没有被Gatsby识别为页面。我需要让Gatsby知道在哪里可以找到添加到项目的内容。为了做到这一点,我将为Gatsby添加一个配置文件。

让我们把我所做的修改提交给Git。

# add changed file for committing
git add .
# commit to repo
git commit -m 'add markdown files'

Gatsby 配置

Gatsby配置是用来定义和配置你可以使用的许多Gatsby插件的。更多关于Gatsby插件生态系统的信息将在稍后公布。现在,我将创建一个文件,还是在终端。

touch gatsby-config.js

这将在项目的根部创建gatsby-config.js ,这样我就可以开始配置Gatsby来读取我之前创建的.mdx 文件。

Gatsby插件

现在我可以安装和配置Gatsby所需的插件,以获得我所创建的文件的来源和显示。我现在将安装它们,并简要地说明它们的用途。

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

现在快速看一下package.json ,显示我安装了以下依赖版本。

"dependencies": {
  "@mdx-js/mdx": "^1.6.22",
  "@mdx-js/react": "^1.6.22",
  "gatsby": "^3.1.1",
  "gatsby-plugin-mdx": "^2.1.0",
  "gatsby-source-filesystem": "^3.1.0",
  "react": "^17.0.1",
  "react-dom": "^17.0.1"
},

有一点需要注意的是,在Gatsby中,不需要在你的组件中导入React 17。但为了完整起见,也为了避免任何混淆,我将在这些例子中包括它。

现在我需要配置gatsby-plugin-mdxgatsby-plugin-mdx 。在gatsby-config.js 文件中,我将添加这个。

module.exports = {
  plugins: [
    `gatsby-plugin-mdx`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content`,
        name: `content`,
      },
    },
  ],
};

提交到现在为止的改动。

git add .
git commit -m 'add gatsby plugins'

Gatsby GraphQL

现在是时候通过使用Gatsby GraphQL客户端GraphiQL来看看我在Gatsby中的文件情况了。你可能已经注意到,如果你一直在关注,CLI指出了两个URL位置来查看项目。

You can now view my-gatsby-blog in the browser.
⠀
  http://localhost:8000/
⠀
View GraphiQL, an in-browser IDE, to explore your site's data and schema
⠀
  http://localhost:8000/___graphql

我现在将使用___graphql (三个下划线)路线来查看文件系统中的文件。

如果这看起来有点吓人,我将试图涵盖所有可能看起来没有什么意义的部分。如果你跟上了,你应该可以把例子复制到GraphiQL浏览器中。

当我打开GraphiQL探索器时,我有几个探索器面板。这是项目中所有可供探索的数据,取决于我在gatsby-config.js 文件中的配置。

GraphiQL查询面板和结果就在这旁边。这是我要写GraphQL查询的地方,以检索我需要的数据。在查询面板的底部还有一个QUERY VARIABLES部分,我稍后会讨论这个问题。

在最右边是GraphQL文档资源管理器。由于GraphQL的严格类型化,这意味着它能够生成自己的数据文档。但这不在这篇文章的范围之内。

用GraphQL查询本地文件

接下来,我将在GraphiQL查询面板中查询我先前添加的文件。在这个查询中,我正在查询文件的字体中定义的标题和日期。

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

如果我们把它弹到查询面板上按下大的播放按钮,我们就会在结果面板上得到一些数据。我们也可以使用左侧面板中的资源管理器来挑选数据。这是我运行查询后得到的结果。

{
  "data": {
    "allMdx": {
      "nodes": [
        {
          "frontmatter": {
            "title": "Hello World - from mdx!",
            "date": "2021-03-06T00:00:00.000Z"
          }
        },
        {
          "frontmatter": {
            "title": "Second Post!",
            "date": "2021-03-07T00:00:00.000Z"
          }
        },
        {
          "frontmatter": {
            "title": "Third Post!",
            "date": "2021-03-08T00:00:00.000Z"
          }
        }
      ]
    }
  },
  "extensions": {}
}

这是一个大的JSON对象,里面有我们在查询中要求的相关信息。我们很快就会看看如何使用这个。现在,这意味着我们可以在Gatsby项目中使用这些数据来制作页面。

网站元数据

gatsby-config.js 文件中,也有一个选项可以指定网站元数据。网站元数据是为了当我想重用像网站标题和描述这样的普通数据时。

当我想在网站上添加元标签以进行搜索引擎优化(SEO)时,这将是非常有用的。(同样,以后会有更多的内容。)现在,我将在gatsby-config.js ,用siteMetadata 对象来定义网站的一些基本信息。

可以像这样在module.exports 中直接定义网站元数据。

module.exports = {
  siteMetadata: {
    title: `My Gatsby Blog`,
    description: `This is my coding blog.`,
  },
  plugins: [
    // configured plugins here
    {
      // empty for brevity
    },
  ],
};

网站元数据对象可能会变得有点大,我发现把它放在自己的对象中可以使它的推理更简单,所以我打算单独定义它。

const siteMetadata = {
  title: `My Gatsby Blog`,
  description: `This is my coding blog.`,
};

然后将siteMetadata 对象添加到Gatsby配置文件中。

const siteMetadata = {
  title: `My Gatsby Blog`,
  description: `This is my coding blog.`,
};

module.exports = {
  siteMetadata,
  plugins: [
    // configured plugins here
    {
      // empty for brevity
    },
  ],
};

现在我可以再次跳到GraphiQL资源管理器上,用下面的查询来查询该网站的元数据。

{
  site {
    siteMetadata {
      title
      description
    }
  }
}

如果你对gatsby-config.js 文件进行修改,停止并重启开发服务器总是一个好主意,所以我将这样做(Ctrl+c,然后是yarn develop ),然后在GraphiQL资源管理器中刷新页面并再次运行查询以获得数据。

{
  "data": {
    "site": {
      "siteMetadata": {
        "title": "My Gatsby Blog",
        "description": "This is my coding blog."
      }
    }
  },
  "extensions": {}
}

制作一个网站元数据钩

现在我在Gatsby文件系统中拥有网站元数据,我可以在任何我想使用Gatsby静态查询钩子的地方查询它useStaticQuery 。我将关闭开发服务器,并在我将以下内容添加到src/pages/index.js 文件后重新启动。

import { graphql, useStaticQuery } from "gatsby";
import React from "react";

export default function IndexPage() {
  const {
    site: { siteMetadata },
  } = useStaticQuery(graphql`
    {
      site {
        siteMetadata {
          title
          description
        }
      }
    }
  `);
  console.log("=====================");
  console.log(siteMetadata);
  console.log("=====================");
  return <h1>Hello World!</h1>;
}

关于那里的一些符号的快速说明:const { site: { siteMetadata }, } 是获取site 查询中的数据的快速方法,我从site 对象中提取siteMetadata 。这被称为去结构化

现在,在我再次启动开发服务器后,我可以到浏览器控制台(在Windows/Linux中是Control+Shift+J,在macOS中是Command+Option+J),在控制台输出中看到siteMetadata 对象。

我得到以下的控制台输出。

=====================
{title: "My Gatsby Blog", description: "This is my coding blog."}
  description: "This is my coding blog."
  title: "My Gatsby Blog"
  __proto__: Object
=====================

不要担心控制台对404页面未找到(net::ERR_ABORTED 404 (Not Found))的警告。我以后再做。

为了避免每次都要写这个查询,我想在一个组件中使用它。我将把它抽象为自己的钩子。

# make a folder for all the hooks to live
mkdir src/hooks
# creathe the file
touch src/hooks/use-site-metadata.js

现在我将在新创建的src/hooks/use-site-metadata.js 文件中加入一个钩子,以按需获得网站的元数据。

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

你可能已经注意到,这个查询与GraphiQL浏览器中的查询不一样。

+ query SITE_METADATA_QUERY {
  site {
    siteMetadata {
      title
      description
    }
  }
}

这是为了给查询命名。因为我将在项目中使用大量的查询,给它们起一个有意义的名字是有意义的。

现在我将在src/pages/index.js 文件中实现新的钩子。

import React from "react";
import { useSiteMetadata } from "../hooks/use-site-metadata";

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

这样就不那么啰嗦了,而且我可以从SITE_METADATA_QUERY 中挑选我想要的项目。

现在是时候把到目前为止所做的改变记录下来了。

git add .
git commit -m 'add site metadata and metadata hook'

继续阅读如何用Gatsby和MDXSitePoint建立一个开发者博客