在本系列的前两篇中,我们已经使用了官方基础模板搭建好了我们的 Gatsby 博客,并通过数据源插件实现了目录文件的读取。
但数据源插件只能够获取到文件本身的信息,却无法读取到文件内容,要读取文件内容,就需要数据源转换插件的帮助了。
前言
在本系列的前两篇中,我们已经使用了官方基础模板搭建好了我们的 Gatsby 博客,并通过数据源插件实现了目录文件的读取。
但数据源插件只能够获取到文件本身的信息,却无法读取到文件内容,要读取文件内容,就需要数据源转换插件的帮助了。
本篇就将以 markdown
文件为例,讲解一下数据源转换插件的使用。
添加示例文档
我们在 pages
目录下新建一个 markdown
文档:markdown-demo.md
,内容可以随意,以下是一个示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
---
title: "示例 Markdown 文档"
date: "2020-01-02"
---
## 这是一个标题
这是一段话,这是**加粗文本**,这是*斜体*,这是行内代码`Javascript`。
> 这是一个引用
这是一个列表:
* 第一项内容
* 第二项内容
* 第三项内容
|
之后,再次访问 http://localhost:8080/myfiles
,可以看到数据源插件已经获取到了该文件,这一过程是即时的,只要我们添加了文件,gatsby-source-filesystem
插件就能够将其添加到 Gatsby 的数据源中:
下面我们要做的,就是读取该 md
文件内容,并将其转换为 HTML 页面。
安装 Markdown 转换插件
gatsby-transformer-remark
插件可以实现将对 Markdown 文件的读取,我们安装该插件:
1
|
yarn add gatsby-transformer-remark
|
之后就像我们安装 gatsby-source-filesystem
插件之后所做的一样,将该插件添加到 gatsby-config.js
文件中去,你的 gatsby-config.js
文件看起来将是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `src`,
path: `${__dirname}/src`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `src`,
path: `${__dirname}/src`,
},
},
`gatsby-transformer-remark`,
...
|
之后重启开发服务器,再次打开 GraphiQL 就可以看到插件已经将 Markdown 文件的内容提到到了数据源中:
我们可以通过 GraphQL 查询查看插件读取到的文件内容:
为博客添加一个文章列表页
我们最终的目标是实现一个博客,而博客一般都会拥有一个包含所有文章的列表页,通过上文内容,我们已经能够获取到目录下所有文件,并能够读取到 Markdown
文件的内容,通过这些,我们已经可以实现一个简单的列表页了。
修改 pages
目录下的 index.js
文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
const Index = ({ data }) => {
console.log(data)
return (
<Layout>
<div>
<h1>文章列表</h1>
<h4>共{data.allMarkdownRemark.totalCount} 篇文章</h4>
{data.allMarkdownRemark.edges.map(({ node }) => (
<div key={node.id}>
<h3>
{node.frontmatter.title}{" "}
<span>
— {node.frontmatter.date}
</span>
</h3>
<p>{node.excerpt}</p>
</div>
))}
</div>
</Layout>
)
}
export default Index
export const query = graphql`
query {
allMarkdownRemark {
totalCount
edges {
node {
id
frontmatter {
title
date(formatString: "DD MMMM, YYYY")
}
excerpt
}
}
}
}
`
|
再次访问 http://localhost:8000
就能看到我们的页面已经发生了变化:
我们继续向 pages
目录中添加 Markdown
文件,首页也会继续发生变化:
将 Markdown 文件渲染为对应的 HTML 页面
前文中,我们通过 gatsby-transformer-remark
插件将获取到了所有的 Markdown
文件,并且将所有文件和内容生成了一个文章列表页,但作为一个博客,需要在点击每一个标题时,跳转到对应的页面,这该如何实现呢?
很容易想到的自然是手动添加对应的 js
文件,比如我们可以添加一个 markdown-demo.js
,然后借助于 GraphQL 查询获取到 Markdown
文件对应的 HTML
内容,渲染为页面。
这种方法自然是可行的,但是如果每添加一篇文章都需要这样处理那也未免太麻烦了,本部分就将讲解如何动态的将 GraphQL 的查询结果映射为页面。
整个过程中需要两个步骤:创建路径、生成内容
为页面创建 slug 或路径
slug
为官方文档中所使用的单词,可以理解为别名,在这里大致和路径类似,本单词本文将不翻译直接使用
本部分中我们将使用多个 Gatsby 提供的 API, Gatsby 提供的所有 API 可以查看官方文档
首先用到的是 onCreateNode
这一API,其中的方法将在每个节点创建或更新时调用,要使用该 API,我们需要编辑项目根目录下的 gatsby-node.js
文件,在其中添加以下内容:
1
2
3
|
exports.onCreateNode = ({node}) => {
console.log(node)
}
|
之后重启服务,可以看到在终端中打印出了所有节点:
从打印内容中我们可以看到能获取到的信息,我们需要做的是为所有 Markdown 类型的节点生成路径。
要实现这一目标,最方便的方式是使用 gatsby-source-filesystem
插件,其提供了 createFilePath
方法,我们修改 onCreateNode
方法:
1
2
3
4
5
6
7
|
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode }) => {
if (node.internal.type === `MarkdownRemark`) {
console.log(createFilePath({ node, getNode, basePath: `pages` }))
}
}
|
重启开发服务器,就可以看到,我们创建的两个 Markdown
文件的路径被打印出来了:
将 slug 信息添加到数据源
我们已经获取到了每一个 Markdown
文档的 slug,那当我们访问这一路径时,如何知道他们对应的是哪一个文件?一个可行的处理方式是将 slug 信息添加到数据源对应的条目中,这样我们就可以通过 GraphQL 查询通过 slug 找到对应的文件了。
我们需要使用使用 createNodeField
这一 API,其功能是扩展某节点,向一个节点添加任意信息,新添加的信息将被存储在 fields
属性下。
改造我们的 onCreateNode
方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions // highlight-line
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
|
重启开发服务器,打开 GraphiQL 浏览器,就可以找到新添加的属性了:
我们可以通过 GraphQL 查询到 slug 对应的节点内容:
有了这些,下一步就是创建页面了。
创建页面
创建页面需要使用 createPages
API,该 API 将在数据源初始化完成,数据源转换插件执行完成后执行,因此可以在其中执行 GraphQL 查询语句。
创建页面的主要操作如下:
- 通过 GraphQL 查询到所有的 slug
- 使用
createPages
所提供的 createPage
方法为每个 slug 生成页面
createPage
接受几个主要参数:
- path: 要生成的页面路径,在页面及页面 GraphQL 查询中均可用
- component: 生成页面的模板
- context: 可选项,要传递的额外信息,传递的信息将在页面 GraphQL 查询中可用
所以,我们先创建一个最简单的模板/src/templates/post.js
,暂不渲染内容,只将传递到页面中的 props
打印出来:
1
2
3
4
5
6
7
8
9
10
11
|
import React from "react"
import Layout from "../components/layout"
export default (props) => {
console.log(props)
return (
<Layout>
<div>Hello blog post</div>
</Layout>
)
}
|
之后,再创建页面,我们修改 gatsby-node.js
方法,增加以下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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/post.js`),
context: {
slug: node.fields.slug,
},
})
})
}
|
我们通过 GraphQL 查询到所有 Markdown 文件的 slug,然后为每一个 slug 生成了一个页面。
重启开发服务器,访问相关页面,可以看到页面已经创建好了,在控制台也打印出了传递到页面中的信息:
渲染页面内容
我们已经为每一个 Markdown 文档生成了页面,但所有页面都只显示了模板内容,我们需要将 Markdown 文档的内容渲染到页面中。
我们已经将 slug 信息等传递到了每一个页面,只需要在模板文件中根据传递的信息进行查询,即可获得文件内容,之后使用获得的内容动态渲染模板文件即可。
我们修改模板文件 /src/templates/post.js
内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
export default ({ data }) => {
const post = data.markdownRemark
return (
<Layout>
<div>
<h1>{post.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</div>
</Layout>
)
}
export const query = graphql`
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
}
}
}
`
|
注意上面的 GraphQL 查询,在 createPage
方法的说明中我们已经提及了,path
变量以及在 context
中的变量均可在这里使用,所以如果我们在这里将 $slug
换成 $path
也是可行的。
再次打开页面,此时已经正常渲染了 Markdown 文件内容:
为文章列表页添加链接
为了方便跳转,给我们前面创建的文章列表页增加跳转链接,修改 pages/index.js
文件内容,增加 GraphQL 查询内容,并将文章标题内容使用 Link
标签包裹:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
import React from "react"
import { Link, graphql } from "gatsby"
import Layout from "../components/layout"
const Index = ({ data }) => {
return (
<Layout>
<div>
<h1>文章列表</h1>
<h4>共{data.allMarkdownRemark.totalCount} 篇文章</h4>
{data.allMarkdownRemark.edges.map(({ node }) => (
<div key={node.id}>
<Link to={node.fields.slug}>
<h3>
{node.frontmatter.title}{" "}
<span>
— {node.frontmatter.date}
</span>
</h3>
</Layout>
<p>{node.excerpt}</p>
</Link>
</div>
))}
</div>
)
}
export default Index
export const query = graphql`
query {
allMarkdownRemark {
totalCount
edges {
node {
id
frontmatter {
title
date(formatString: "DD MMMM, YYYY")
}
fields {
slug
}
excerpt
}
}
}
}
`
|
再次打开首页,已经可以跳转到生成的页面了:
总结
通过本篇内容,我们了解了 Gatsby 如何利用数据源转换插件来读取文件内容并将其加入数据源,以及如何利用数据源中的内容动态生成页面。到目前为止,我们已经实现了一个基本的博客功能,现在只要在 pages
目录下添加 Markdown 文件,就会自动生成对应的页面,并加入到首页的文章列表中。
当然,目前我们的博客还比较简陋,因为我们未添加任何样式,在本系列的后续内容中,将会有专门学习 Gatsby 样式相关的知识,并一起来进行网站的美化。在下一部分,我们将了解 Strapi 相关的知识,了解如何从远程 API 获得数据并进行渲染。