欢迎来到Gatsby(或GatbyJS),一个利用GraphQL和React的静态网站生成的强大框架。随着时间的推移,Gatsby的知名度越来越高,越来越多的人开始使用它。其中一个原因是它能够帮助你直接而快速地创建一个简单的静态网站。
你只需拿出他们的一个入门套件,在Netlify上建立一个账户,然后砰的一声,你就有了一个网站。
我也曾经用这种方式建立网站。我看到了一个不费吹灰之力就能立即创建一个博客的机会,就去做了。但后来当我试图给我的博客添加一些复杂的逻辑时,问题出现了。我并不真正了解幕后发生了什么。盖茨比在做什么,为什么它看起来如此神奇?出于这个原因,我决定退一步,从头开始建立一个Gatsby博客。
在这篇文章中,我将分享如何从一个简单的Gatsby启动包建立一个博客,并解释Gatsby是如何在幕后工作的。这篇文章应该能让你清楚地了解这个用于建立静态网站的意见框架是如何运作的,以及你如何定制它以满足你的需求。
所需的工具
在我们开始之前,你将需要在你的机器上设置一些东西。
快速检查你的终端中是否有Node.js与node --version 。你应该用Node.js得到NPM,但用npm --version ,快速检查一下。如果这两个命令都返回一个版本,你就可以开始了。如果没有,你可以按照NPM网站上的说明进行操作。
现在我们已经得到了这个方法,我们可以在本地安装Gatsby CLI。Gatsby CLI需要调用命令,以生成我们的 repo,启动我们的开发服务器,甚至构建我们博客的生产代码。要安装它,你可以做以下工作:
npm install -g gatsby-cli
快速验证Gatsby是否被安装,gatsby --version 。在安装并检查了我们开始开发所需的一切之后,我们就可以一头扎进它了。
初始化Repo
为了启动博客,我们将使用Gatsby的简单的hello-world启动工具包。这个工具包是一个简约的工具包,一开始并没有给你带来什么。我们这样做是为了更好地了解Gatsby和它的内部结构。如果你想快速建立你的网站,你可以随时选择加入Gatsby的其他套件。
让我们创建我们的工作目录:
gatsby new my-new-blog https://github.com/gatsbyjs/gatsby-starter-hello-world
在Gatsby CLI完成安装一切后,我们可以用cd my-new-blog 进入我们博客的目录,用gatsby develop 启动服务器。这个命令将为我们设置一堆东西,并使我们的网站可以在http://localhost:8000/。
如果你打开它,你可以看到通常的 "Hello world!"。Yay!我们的博客活蹦乱跳了。但是刚才发生了什么?是的,我们在终端运行了几个命令,在我们的新博客上向整个世界问好,但这是怎么发生的呢?让我们从查看src/pages/index.js 文件开始:
import React from "react"
export default function Home() {
return <div>Hello world!</div>
}
一个非常简单的React组件,它只是向世界问好。当我们运行gatsby develop ,它将识别src/pages/index.js 文件,并将其视为我们博客根页面的逻辑。Gatsby自己会弄清楚这一点,这就是为什么我们在访问索引页时看到 "Hello world "的文字。
如果你试图对这个文件做一些改变,正在观察我们目录中所有变化的服务器将自动更新网站。多么整洁啊!
只拥有一个文件并使用Gatsby听起来似乎有些矫枉过正(而且确实如此)。这就是为什么我们会在我们的博客上添加东西,并沿途解释这个过程。
Gatsby和GraphQL
GraphQL是一种语言,用于查询你的API并获得你所需要的数据,非常强调做一个请求。能够在一个请求中收集大量的数据,意味着你不需要多次往返于你的服务器。
除了这些,你还需要定义一个模式,你可以在此基础上进行查询。Gatsby很聪明地做到了这一点。为了更好地理解Gatsby和GraphQL是如何工作的,让我们首先向我们的网站添加一些数据。
你可能想添加的第一件事是关于你的博客的一些一般信息。例如,你的网站的标题和描述对任何访问的人都是有用的。让我们在gatsby-config.js 文件中定义这些数据。我们将在以后的一些其他工作中使用这个文件,但现在,让我们把它做成这样:
module.exports = {
/* Your site config here */
siteMetadata: {
title: "My New Blog",
description: "This is my awesome blog I made from scratch!",
},
plugins: [],
}
现在,让我们在浏览器中向我们的用户显示这些数据。如果我们进入src/pages/index.js ,我们可以添加一个简单的GraphQL查询来获取我们刚刚添加的数据:
import React from "react"
import { graphql } from "gatsby"
export default function Home({ data }) {
const { title, description } = data.site.siteMetadata
return (
<div>
<h1>{title}</h1>
<p>{description}</p>
</div>
)
}
export const pageQuery = graphql`
query MetadataQuery {
site {
siteMetadata {
title
description
}
}
}
`
在文件的底部,我们将放入GraphQL查询,以获取我们先前添加到gastby-config.js 的标题和描述。GraphQL使这些数据可用,而Gatsby负责获取。我们不需要明确地调用查询。获取工作将在构建过程中为我们完成。
这种高效的构建过程是Gatsby被定义为 "令人厌恶的快速 "的原因,因为它在数据加载到浏览器之前就完成了所有页面数据的获取。当然,你可以在你的组件中写一些额外的获取逻辑,但这些默认情况下是不会被加载的。
gatsby-config.js Gatsby将在构建过程中(当我们运行gatsby develop )通过识别src/pages/index.js 里面有一些GraphQL逻辑来使这些数据对GraphQL可用。同时,我们添加到siteMetadata 对象将对GraphQL可用。
如果你愿意,你可以在这里看到GraphiQL工具中还有什么可用:http://localhost:8000/___graphiql。当你运行服务器时,这在本地是可用的。
我们成功地将一些数据拉入我们的单页博客,而且我们成功地弄清楚了Gatsby是如何处理数据的。但这仍然不接近有人会读的博客。我们需要让它变得友好一些。让我们添加一张可爱的狗的图片,为在Gatsby中添加和读取文件铺平道路。
管理图片
为了使我们的博客更有吸引力,我们将添加一张图片。我将把我在网上找到的一张可爱的狗的图片添加到一个新的目录:src/images 。
最后的路径应该是这样的:src/images/cute-dog.jpg 。为了以盖茨比的方式展示这张图片,我们需要安装一个盖茨比插件。插件是有用的库,使我们在建立Gatsby网站时生活更轻松,这里有很多有用的插件,你可以浏览。主要的想法是不要为通常的任务和功能编写你的解决方案,如SEO、RSS、离线支持,以及--你猜对了--图片。
我们要使用的这个叫做gatsby-source-filesystem。通过它的名字,你可以知道它与文件有某种关系。你是对的。这个插件将帮助我们使用GraphQL对那张可爱的狗的图片进行查询。
要安装它,请运行这个:
npm install --save gatsby-source-filesystem
然后,如果你记得gatsby-config.js ,我们将用它来告诉Gatsby我们有一个插件,像这样:
module.exports = {
/* Your site config here */
siteMetadata: {
title: "Myasd New Blog",
description: "This is my awesome blog I made from scratch!",
},
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images/`,
},
},
],
}
除了我们在上一步中添加的数据,我们现在添加了关于gatsby-source-filesystem 插件的细节。在选项中,我们指定了源的名称--"images"--并指定了我们的图片将所在的路径。
定义一个名称是一个重要的步骤,你可能想知道为什么我们需要设置这个。Gatsby可以从不同的来源读取数据--不仅仅是我们的文件系统。另一个来源的例子是WordPress或Contentful。所以,我们的文件系统只是一种可能,我们可以在那里存储我们的图片或博客文章,设置一个name "images "可以帮助我们区分不同的来源,并在将来以另一种方式处理它们。
总之,在我们添加了这个插件之后,如果我们在终端看一下我们正在运行的服务器,应该有以下信息:
warn develop process needs to be restarted to apply the changes to gatsby-config.js
我们需要重新启动服务器,以便新的插件被Gatsby接收。让我们重新启动我们的服务器。众所周知,Gatsby在使用npm install 时有问题,所以如果你在添加gatsby-source-filesystem 插件时出现错误,请确保使用rm -rf node_modules && npm install 或npm install react react-dom gatsby 。
服务器启动后,我们可以尝试在我们的src/pages/index.js 文件中获取图片:
import React from "react"
import { graphql } from "gatsby"
export default function Home({ data }) {
const { title, description } = data.site.siteMetadata
return (
<div>
<h1>{title}</h1>
<p>{description}</p>
<img alt="Cute dog" src={data.image.publicURL} />
</div>
)
}
export const pageQuery = graphql`
query MetadataQuery {
site {
siteMetadata {
title
description
}
}
image: file(base: { eq: "cute-dog.jpg" }) {
publicURL
}
}
`
我们扩展了我们的GraphQL查询,以搜索一个名称为 "cut-dog.jpg "的文件。然后我们告诉GraphQL将该文件别名为image,这样我们就可以在以后的代码中有意义地引用它。最后,在我们的代码中,我们放了一个JSX图像标签,并在那里引用了图像的公共URL。
然后,我们得到了图片,显示在我们的页面上。

我们建立的东西很好,但我们的博客上仍然没有任何博客文章,所以让我们在下一章中添加它们。
处理Markdown文件
自从Jekyll时代以来,使用Markdown(一种轻量级的标记语言)来编写内容变得很流行。我们将为我们的博客做同样的事情。让我们在src/blog/my-first-post.md 里面添加我们的第一篇博文:
---
title: Awesome Blog Post Title
author: Nikola
date: 2020-07-15
---
## Introduction to my blog post
Great content of my first blog
顶端是代表文章一般信息的前述事项。在这之后是内容。让我们在开始的时候不要太疯狂,使用简单的内容,我们可以在以后建立。
由于我们添加了src/blog 目录,并且我们计划将所有的Markdown文件存储在那里,让我们在我们的gatsby-config.js 中定义这个:
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images/`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `blog`,
path: `${__dirname}/src/blog/`,
},
},
],
现在我们有2个Gatsby应该知道的地方:src/images 和src/blog 。我们可以继续使用我们用来获取图片的类似查询,但让我们试试Gatsby提供的一个插件,叫做gatsby-transformer-remark 。这个插件允许你使用Remark,一个Markdown处理器,并减少了解析所有Markdown文件的时间。
你可以用以下方式安装这个插件:
npm install --save gatsby-transformer-remark
把它放在gatsby-config.js 里面:
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images/`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `blog`,
path: `${__dirname}/src/blog/`,
},
},
`gatsby-transformer-remark`,
],
现在我们重新启动gatsby develop 服务器。很完美!我们要做的就是让我们的博文显示出来。为了让我们的博客文章显示出来,我们将在src/pages/blog.js 中为它建立一个单独的页面:
import React from "react"
import { graphql } from "gatsby"
export default function Blog({ data }) {
const { posts } = data.blog
return (
<div>
<h1>My blog posts</h1>
{posts.map((post) => (
<article key={post.id}>
<h2>post.frontmatter.title</h2>
<small>
{post.frontmatter.author}, {post.frontmatter.date}
</small>
<p>{post.excerpt}</p>
</article>
))}
</div>
)
}
export const pageQuery = graphql`
query MyQuery {
blog: allMarkdownRemark {
posts: nodes {
frontmatter {
date(fromNow: true)
title
author
}
excerpt
id
}
}
}
`
在这里,我们使用allMarkdownRemark 来查询所有的Markdown文件,这可以从我们安装的插件gatsby-transformer-remark 。
这使我们能够直接获取包括date 、title 和author 的前台事项。我们还可以传入一个参数给date(fromNow: true) ,它可以显示相对时间,例如 "5天前"。如果没有gatsby-transformer-remark ,我们将不得不以某种方式来解析这个。
然后,在博客组件中,我们向用户列出所有的博客文章和它们的信息。注意到我们没有任何博客文章的链接,但我们将在后面添加。
幸运的是,Gatsby会接收所有这些变化,并在/blog ,使一个新的页面可用。如果我们进入/blog ,我们应该看到像这样的东西:
你可以在src/blog 里面添加另一个Markdown文件,它将自动被盖茨比拾取。

非常酷!但我如何访问一个单独的博客?但我如何访问一个独立的博客文章呢?嗯,这就是事情变得棘手的地方。请继续阅读以了解如何做到这一点:
动态地创建页面
为了创建所有博客文章的链接,我们需要告诉Gatsby为我们创建它们。这一部分是你应该保持最多注意力的地方,因为我们现在可以玩玩Gatsby的内部程序了。到目前为止,我们已经安装了插件,添加了数据,并写了一些Markdown和JSX,但现在是时候开始行动了。
玩转节点
首先,让我们介绍一下Gatsby中的 "Node "一词。正如Gatsby文档所说,"节点 "是Gatsby的数据系统的中心。我们在数据方面所添加的一切,在Gatsby中都被表示为节点对象。
如果你回头看看我们为获取所有Markdown帖子所做的查询,你可以看到我们查询的是nodes 。 gatsby-source-filesystem 它 "扫描 "我们告诉它的目录,并为这些目录中的每个文件创建节点。然后gatsby-markdown-remark ,解析节点内的数据,并为这些节点对象添加额外的字段。
此外,Gatsby还根据这些节点的内容,为它们总结出一个GraphQL模式。哇,可能一下子要接受很多东西。我敢打赌,如果我们看到一个例子,就会更容易。
让我们先为我们的每篇博客文章创建一个slug。例如,由于我们添加了src/blog/my-first-post.md ,我们希望当我们访问/my-first-post ,就能看到这篇博文。要做到这一点,我们将利用createFilePath ,该函数与gatsby-source-filesystem 一起,将为我们做这个。
在刚刚创建了一个lug之后,我们还将创建一个我们在上面一节中描述的节点。创建lug的最好地方是Gatsby暴露的[onCreateNode](https://www.gatsbyjs.org/docs/node-apis/#onCreateNode) API。像我们这样的插件和用户可以使用onCreateNode 来定制节点或在节点被创建或更新时做其他事情。
像这样的逻辑被放在gatsby-node.js ,一个我们将创建的新文件中:
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
如果你继续前进并重新启动服务器,迅速在本地打开GraphiQL工具:(http://localhost:8000/___graphiql),用这个查询来查询蛞蝓:
{
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
你应该看到我们之前添加的那两篇Markdown博客文章的slug。

相当整齐我们告诉Gatsby用字段slug 来扩展每个Markdown节点,我们可以用它来把用户重定向到我们的博客文章。
但是等等,如果你试图访问/another-blog-post ,你会得到404。这很好,因为我们没有创建实际的页面,我们只是生成了slugs。请跟随下一节学习如何做到这一点。
以编程方式生成页面
我们很幸运,Gatsby暴露了[createPages](https://www.gatsbyjs.org/docs/node-apis/#createPages) API,这使我们能够做到这一点。但是,在我们继续添加更多的逻辑到gatsby-node.js ,我们需要做一些其他的事情。由于Gatsby页面使用React,每个页面需要有一个React组件来渲染数据。为此,我们将为我们的博客文章创建一个组件,并把它放在src/templates/blog-post.js:
import React from "react"
export default function BlogPost() {
return <div>Hello blog post</div>
}
正如你所看到的,它是一个直接的组件,没有利用任何数据。我们以后会改变这一点。我们必须有某种React组件来告诉Gatsby来渲染我们的博客文章。
有了这个,让我们添加一些逻辑到gatsby-node.js ,以创建实际的博客文章页面:
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
`)
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
// Data passed to context is available
// in page queries as GraphQL variables.
slug: node.fields.slug,
},
})
})
}
我们为我们的页面创建逻辑添加了似乎是很多的代码。简而言之,这就是所发生的事情:
- 我们查询所有的Markdown页面并获得它们的slug
- 然后,我们遍历所有的页面并调用
createPage - 在
createPage,我们指定页面的路径,我们刚刚添加的组件src/templates/blog-post.js,并且我们传入上下文,我们将在稍后解释。
如果你继续重启你的服务器并尝试访问http://localhost:8000/my-first-post/,你应该看到我们之前在组件中添加的简单的 "Hello blog post "文本。相反,我们应该看到我们放在Markdown文件中的内容。
让我们把我们的src/templates/blog-post.js 改成这样:
import React from "react"
import { graphql } from "gatsby"
export default function BlogPost({ data }) {
const post = data.markdownRemark
return (
<div>
<h1>{post.frontmatter.title}</h1>
<small>{post.frontmatter.date}</small>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</div>
)
}
export const query = graphql`
query BlogQuery($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
}
}
}
`
现在我们使用Markdown文件中的数据,而不是仅仅显示 "Hello blog post"。有一点需要注意的是,我们使用了我们在gatsby-node.js 中创建页面时指定的$slug 变量。
Slug也可以作为BlogPost组件里面的props。使用我们在createPage 中定义的slug ,然后我们查询Markdown节点的匹配节点。然后,在组件中,我们使用我们在查询中指定的字段来正确显示博客文章。如果你现在打开http://localhost:8000/my-first-post/,你应该看到你的博文以你在Markdown文件中定义的方式全亮。
这是相当紧张的,试图创建slugs和页面,并显示文件内的数据。祝贺你能走到这一步!作为蛋糕上的一颗樱桃,让我们通过从我们的博客页面链接到这些博客文章来把这一切联系起来。
完工
如你所知,我们创建了/blog ,但我们没有从主页链接到它。让我们通过添加到src/pages/index.js 来快速完成:
<Link to="/blog">Read my blog</Link>
然后,让我们从我们的博客页面链接到每篇博客文章:
import React from "react"
import { graphql, Link } from "gatsby"
export default function Blog({ data }) {
const { posts } = data.blog
return (
<div>
<h1>My blog posts</h1>
{posts.map((post) => (
<article key={post.id}>
<Link to={post.fields.slug}>
<h2>{post.frontmatter.title}</h2>
</Link>
<small>
{post.frontmatter.author}, {post.frontmatter.date}
</small>
<p>{post.excerpt}</p>
</article>
))}
</div>
)
}
export const pageQuery = graphql`
query MyQuery {
blog: allMarkdownRemark {
posts: nodes {
fields {
slug
}
frontmatter {
date(fromNow: true)
title
author
}
excerpt
id
}
}
}
`
我们从Gatsby导入了Link 组件,它将为我们处理路由问题。然后,我们在GraphQL查询中查询slug。之后,我们使用该lug来形成每个博客文章的链接。就这样了!你刚刚从头开始使用Gatsby建立了一个简单的博客。
可以尝试的想法
我们建立的博客文章看起来还远远没有完成,但从现在开始应该很容易了。你已经具备了扩展博客的所有基本条件。下面是你可以尝试的一些想法:
- 将博客文章的路径转换为从
/blog/*(提示:当我们调用createPage,编辑path变量 ) - 从主页上显示所有文章
- 给你的博客添加一些风格
- 部署它,让全世界都知道你的想法!
总结
为制作它而喝彩!如果你走到这一步,你应该已经学会了如何建立一个简单的博客,并对Gatbsy的内部结构有了高度的了解。节点的概念和Gatsby如何处理数据对你来说现在应该是轻而易举的。另外,我希望Gatsby的魔力对你来说有点解密的意思。
总而言之,Gatsby是一个快速建立静态网站的伟大工具。还有许多其他的工具和方法,但理解其本质和幕后的东西总是朝着正确的方向迈出一步。我希望这些知识在未来能坚持下去,并帮助你建立伟大的网站。