1. 概述
我写博客经历了以下阶段:
- 2016 年,记在一本纸质笔记本上
- 2017 年,发表在 QQ 空间/微信公众号
- 2018 ~ 2021 年,发表在 csdn,至今还有很多平台盗用我的文章
- 2022 年,开始自建博客
由于文档使用 markdown 编写非常高效,且有利于版本管理,所以就直接选用了静态博客。
本篇博客由 gatsby 构建而成,当初选型 gatsby 看重于其构建的灵活性。 时至今日,gatsby 已非主流博客搭建工具,但亦足够满足需求。
2. gatsby
官方文档:https://www.gatsbyjs.cn/docs/
重点说明当前博客用到或者需要用到的特性。
2.1 项目结构
在 Gatsby 项目中,你会看到这些文件和文件夹,可能是一些,可能是全部:
|-- /.cache
|-- /plugins
|-- /public
|-- /src
|-- /pages
|-- /templates
|-- html.js
|-- /static
|-- gatsby-config.js
|-- gatsby-node.js
|-- gatsby-ssr.js
|-- gatsby-browser.js
一些值得注意的文件和它们的定义:
gatsby-config.js
——配置 Gatsby 站点的选项,比如项目标题和项目描述的元数据,插件等等gatsby-node.js
——实现了 Gatsby 的 Node.js API,来自定义和扩展影响构建过程的设置gatsby-browser.js
——使用 Gatsby 的浏览器 API 自定义和扩展影响浏览器的的设置gatsby-ssr.js
——使用 Gatsby 的服务端渲染 API 自定义影响服务端渲染的默认设置
2.2 静态创建页面
Gatsby 的核心自动把 src/pages
中的 React 组件转变为页面和 URL。比如:在 src/pages/index.js
和 src/pages/about.js
中的组件,会为网站的索引页(/
)和 关于页(/about
)自动创建基于文件名的页面。
操作步骤:
- 如果你没有
src/pages
这个目录的话,创建它。 - 添加一个组件文件在这个 pages 目录里:
import React from "react"
const AboutPage = () => (
<main>
<h1>About the Author</h1>
<p>Welcome to my Gatsby site.</p>
</main>
)
export default AboutPage
- 运行
yarn dev
以启动开发服务器。 - 在浏览器中访问你的新页面:
http://localhost:8080/about
2.3 动态创建页面
你可以使用 Gatsby 提供的辅助方法,在 gatsby-node.js
文件中以编程方式创建页面。
操作步骤:
- 在
gatsby-node.js
文件中,为createPages
添加一个输出(export)
exports.createPages = ({ actions }) => {
// ...
}
- 从可用操作中解构
createPage
操作,以便可以单独调用它,并添加或获取数据
exports.createPages = ({ actions }) => {
const { createPage } = actions
// pull in or use whatever data
const dogData = [
{
name: "Fido",
breed: "Sheltie",
},
{
name: "Sparky",
breed: "Corgi",
},
]
}
- 遍历
gatsby-node.js
中的数据,并为每次调用将路径,模板和上下文(在 props 的 pageContext 中传入的数据)提供给createPage
exports.createPages = ({ actions }) => {
const { createPage } = actions
const dogData = [
{
name: "Fido",
breed: "Sheltie",
},
{
name: "Sparky",
breed: "Corgi",
},
]
dogData.forEach(dog => {
createPage({
path: `/${dog.name}`,
component: require.resolve(`./src/templates/dog-template.js`),
context: { dog },
})
})
}
- 创建一个 React 组件作为你的页面模板,这个组件在
createPage
中已经被使用了
src/templates/dog-template.js
import React from "react"
export default ({ pageContext: { dog } }) => (
<section>
{dog.name} - {dog.breed}
</section>
)
- 运行
yarn dev
并导航到一个你创建的页面路径(例如http://localhost:8080/Fido
)来查看传给它的数据是否成功显示在页面上
2.4 页面跳转
gatsby 中不要使用 a 标签跳转,否则不会加上网站根目录。
<Link to="/">Vance’s Blog</Link>
2.5 全局样式
操作步骤:
- 添加一个全局 CSS 文件
src/styles/global.css
并输入以下内容:
/* src/styles/styles/global.css */
html {
background-color: lavenderblush;
}
p {
color: maroon;
}
- 在
gatsby-browser.js
文件中引入这个全局 CSS 文件,像这样:
import "./src/styles/global.css"
2.6 局部样式
局部样式只作用在组件当中,使用 scss 加作用域完成。
Gheader.jsx 组件中:
import "../styles/gheader.scss"
const Gheader = () => {
return (
<header id="g-header">
<div className="header__left"></div>
</header>
)
}
gheader.scss 中:
#g-header {
position: fixed;
top: 0;
left: 0;
.header__left {
display: flex;
}
}
2.7 pageQuery
pageQuery
是一个内置组件,用于从 Gatsby 页面的数据层检索信息。每个页面可以有一个页面查询。它可以为查询中的变量接受 GraphQL 参数。
使用场景: 博客文章页、产品详情页,根据 URL 中参数动态查询数据
操作步骤:
- 从
gatsby
中引入graphql
。 - 导出一个名为
query
的变量,并设置它的值为graphql
模版加上用两个反引号括起来的查询命令。 - 把
data
作为 prop 传入到组件中。 data
变量保存了被查询的数据,并且可以被 JSX 引用来生成 HTML。
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
export const query = graphql`
query HomePageQuery {
site {
siteMetadata {
title
}
}
}
`
const IndexPage = ({ data }) => (
<Layout>
<h1>{data.site.siteMetadata.title}</h1>
</Layout>
)
export default IndexPage
2.8 useStaticQuery
StaticQuery 已经废弃,使用 useStaticQuery 替换。
useStaticQuery
hook 接收一个 GraphQL 查询命令并返回所查询的数据。它可以被存储到一个变量里,在之后的 JSX 模版中使用。
使用场景: 不能传递参数,数据是构建时就确定的。显示全站配置、导航栏、footer 等不会变化的内容
操作步骤:
- 从
gatsby
中引入useStaticQuery
和graphql
以使用 hook 来查询数据。 - 在一个无状态功能性组件的一开始,使用
useStaticQuery
和graphql
查询命令作为其参数,给一个变量赋值。 - 在组件返回的 JSX 代码中,你可以引用这个变量来处理返回的数据。
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
const NonPageComponent = () => {
const data = useStaticQuery(graphql`
query NonPageQuery {
site {
siteMetadata {
title
}
}
}
`)
return (
<h1>
Querying title from NonPageComponent: {data.site.siteMetadata.title}{" "}
</h1>
)
}
export default NonPageComponent
2.9 给节点添加数据
扩展另一个节点。新的节点字段放置在扩展的节点对象下的 fields
键下。
- 新增属性
gitModifiedTime
exports.onCreateNode = ({ node, actions }) => {
const { createNode, createNodeField } = actions
// Transform the new node here and create a new node or
// create a new node field.
const gitDate = execSync(
`git log -1 --date=format:"%Y-%m-%d %H:%M" --format="%ad" "${filePath}"`
)
.toString()
.trim()
createNodeField({
node,
name: "gitModifiedTime",
value: gitDate,
})
}
- 查询
gitModifiedTime
属性
export const pageQuery = graphql`
query BlogPostBySlug($id: String!) {
markdownRemark(id: { eq: $id }) {
id
frontmatter {
title
date(formatString: "YYYY-MM-DD")
description
}
fields {
slug
gitModifiedTime
}
}
}
`
2.10 文档搜索
使用了 search-data.json
文件保存了博客内容,再通过 fuse.js
进行搜索。
操作步骤:
- 在
gatsby-node.js
中增加search-data.json
生成的逻辑。
exports.onPostBuild = async ({ graphql }) => {
const result = await graphql(`
{
allMarkdownRemark {
nodes {
fields {
slug
}
frontmatter {
title
date(formatString: "YYYY-MM-DD")
}
rawMarkdownBody
}
}
}
`)
const posts = result.data.allMarkdownRemark.nodes
const searchData = posts.map(post => ({
title: post.frontmatter.title,
slug: post.fields.slug,
date: post.frontmatter.date,
content: markdownToPlainText(post.rawMarkdownBody),
}))
const filePath = path.join(__dirname, "public", "search-data.json")
fs.writeFileSync(filePath, JSON.stringify(searchData))
}
- 在页面中加载
search-data.json
并执行搜索
import Fuse from "fuse.js"
fetch(getPathPrefix() + "search-data.json")
.then(res => res.json())
.then(data => {
const fuseInstance = new Fuse(data, {
keys: [
{ name: "title", weight: 0.5 },
{ name: "content", weight: 0.5 },
],
includeMatches: true,
threshold: 0.3,
ignoreLocation: true,
})
})
const search = query => {
if (fuse && query.length > 0) {
const searchResults = fuse.search(query)
setResults(searchResults)
} else {
setResults([])
}
}
3. 构建发布
- 使用部署命令后,将会自动构建 gh-pages,并推送到:https://github.com/fanqunxing/vance/tree/gh-pages
yarn deploy
- 检查 github 构建流水。https://github.com/fanqunxing/vance/actions
4. 规范
4.1 新建文章
按时间新建文章,并在文章内放置资源文件。
blog/
└── 2025/
└── 05/
└── my-first-blog/
├── index.md
├── cover.jpg
└── diagram.png
4.2 FrontMatter
---
title: 搭建这个博客
date: "2025-05-31"
description: 讲述本博客的搭建与维护
tags: ["静态博客", "gatsby"]
draft: true
---
draft 字段为草稿,完成后去掉标记。
4.3 正文目录
从二级标题 ##
开始正文结构,用 阿拉伯数字编号 提高逻辑层次清晰度。
## 1. 简介
简要说明文章目的。
## 2. 为什么需要标题规范
讲理由、背景。
## 3. 推荐的写法
### 3.1 正确使用层级
### 3.2 避免过长标题
## 4. 总结