Gatsby

技术栈
前端框架
reactssggraphqljamstackplugin-ecosystem

概览

Gatsby 技术栈概览

Gatsby 是基于 React 的静态站点生成器(SSG),曾是 JAMStack 运动的标杆。它通过 GraphQL 数据层统一管理各种数据源(CMS、Markdown、API、数据库),在构建时生成静态 HTML,运行时通过 React 水合为 SPA。

Gatsby 是什么?

  • React SSG 框架,构建时生成静态页面
  • GraphQL 数据层抽象所有数据源
  • 插件生态极其丰富(3000+ 插件)
  • Gatsby Cloud 提供优化构建和 CDN

解决什么问题?

  • 需要 SEO 但想用 React 开发
  • 博客/文档站/公司官网
  • 多数据源聚合展示(CMS + Markdown + 外部 API)
  • 毕设个人主页、技术博客

关键特性:

  • gatsby-source-* 系列插件
  • 图片优化(gatsby-plugin-image)
  • 增量构建(Gatsby v4+)
  • gatsby-node.js 编程式页面创建
  • Head API(v5+ 原生 SEO)

安装

环境准备

  • 操作系统:macOS / Linux / Windows(WSL 推荐)
  • Node.js:>= 18.x(推荐 20 LTS)
  • 包管理器:npm / yarn
  • 全局 Gatsby CLI(可选):npm install -g gatsby-cli
  • Python / C++ 编译工具链(安装某些 native 插件时需要):
    • Windows:npm install -g windows-build-tools
    • macOS:xcode-select --install
    • Linux:sudo apt install build-essential

安装命令

使用 Gatsby CLI 创建

# 全局安装 CLI
npm install -g gatsby-cli

# 创建项目
gatsby new my-gatsby-site

# 选择模板后进入
cd my-gatsby-site
npm run develop

使用特定模板

gatsby new my-blog https://github.com/gatsbyjs/gatsby-starter-blog
gatsby new my-portfolio gatsbyjs/gatsby-starter-default

手动安装(现有项目)

npm install gatsby react react-dom

package.json 添加脚本:

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

常见安装问题

1. gatsby develop 启动慢

  • 解决:Gatsby CLI 5+ 已大幅改善,确保使用最新版本。首次构建需下载远程数据(CMS 等)

2. sharp 安装失败

  • 现象Error: Something went wrong installing the "sharp" module
  • 解决
    # 使用国内镜像
    npm config set sharp_binary_host https://npmmirror.com/mirrors/sharp
    npm config set sharp_libvips_binary_host https://npmmirror.com/mirrors/sharp-libvips
    npm install --include=optional sharp
    

3. GraphQL schema 不更新

  • 解决gatsby clean 清除缓存后重新 gatsby develop

4. 内存不足

  • 现象:构建大站时 OOM
  • 解决NODE_OPTIONS=--max-old-space-size=4096 gatsby build

示例

Gatsby Hello World — 静态博客站点

目标

创建一个 Gatsby 博客站点,展示 GraphQL 数据查询、静态生成和页面路由。

完整代码

项目初始化

gatsby new my-blog
cd my-blog
npm run develop  # 访问 http://localhost:8000

首页 src/pages/index.js

import * as React from "react";
import { graphql, Link } from "gatsby";

// GraphQL 查询在构建时执行,结果注入 data prop
export const query = graphql`
  query IndexPageQuery {
    allMdx(sort: { frontmatter: { date: DESC } }) {
      nodes {
        id
        frontmatter {
          title
          date(formatString: "YYYY-MM-DD")
          description
        }
        fields { slug }
      }
    }
    site {
      siteMetadata {
        title
        description
      }
    }
  }
`;

const IndexPage = ({ data }) => {
  const posts = data.allMdx.nodes;
  const { title: siteTitle, description } = data.site.siteMetadata;

  return (
    <main style={mainStyle}>
      <h1>{siteTitle}</h1>
      <p style={{ color: "#555" }}>{description}</p>

      <h2>📝 最新文章</h2>
      <ul style={{ listStyle: "none", padding: 0 }}>
        {posts.map((post) => (
          <li key={post.id} style={cardStyle}>
            <Link to={post.fields.slug} style={{ textDecoration: "none", color: "#333" }}>
              <h3 style={{ margin: 0 }}>{post.frontmatter.title}</h3>
              <p style={{ color: "#666", fontSize: "0.9rem" }}>{post.frontmatter.description}</p>
              <small style={{ color: "#999" }}>{post.frontmatter.date}</small>
            </Link>
          </li>
        ))}
      </ul>
    </main>
  );
};

export default IndexPage;

const mainStyle = {
  maxWidth: 680, margin: "40px auto", padding: "0 16px",
  fontFamily: "Georgia, serif",
};
const cardStyle = {
  padding: "20px 0", borderBottom: "1px solid #eee",
};

Gatsby 配置 gatsby-config.js

module.exports = {
  siteMetadata: {
    title: "我的技术博客",
    description: "用 Gatsby 构建的静态博客",
    siteUrl: "https://example.com",
  },
  plugins: [
    "gatsby-plugin-image",
    "gatsby-plugin-sharp",
    {
      resolve: "gatsby-source-filesystem",
      options: { name: "posts", path: `${__dirname}/content/posts/` },
    },
    {
      resolve: "gatsby-plugin-mdx",
      options: { extensions: [".mdx", ".md"] },
    },
  ],
};

文章模板 src/templates/blog-post.js

import * as React from "react";
import { graphql, Link } from "gatsby";
import { MDXRenderer } from "gatsby-plugin-mdx";

export const query = graphql`
  query BlogPostQuery($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      frontmatter {
        title
        date(formatString: "YYYY年MM月DD日")
        description
      }
      body
    }
  }
`;

const BlogPost = ({ data }) => {
  const { frontmatter, body } = data.mdx;

  return (
    <main style={{ maxWidth: 720, margin: "40px auto", padding: "0 16px" }}>
      <Link to="/" style={{ color: "#6366f1" }}>← 返回首页</Link>
      <h1>{frontmatter.title}</h1>
      <p style={{ color: "#888" }}>{frontmatter.date}</p>
      <article>
        <MDXRenderer>{body}</MDXRenderer>
      </article>
    </main>
  );
};

export default BlogPost;

示例 MDX 文章 content/posts/hello-world.mdx

---
title: "我的第一篇博客"
date: 2024-01-15
description: "使用 Gatsby 和 MDX 写博客就是这么简单"
---

# 你好,世界!

Gatsby 让静态博客变得简单而强大。

- ✅ **Markdown/MDX** 编写内容
- ✅ **GraphQL** 统一数据层
- ✅ **自动优化** 图片和性能

运行步骤

npm run develop   # 开发模式,http://localhost:8000
npm run build     # 构建生产版本到 public/
npm run serve     # 预览生产构建

预期输出

  • 首页显示文章列表(日期、标题、描述)
  • 点击文章进入详情页,MDX 内容被渲染
  • 所有页面均为纯静态 HTML
  • GraphQL 浏览器:http://localhost:8000/___graphql 可交互式查询数据

Gatsby GraphQL 数据页

目标

展示 Gatsby 核心模式:通过 GraphQL 查询 siteMetadata 和文件数据,在页面中渲染。这是 Gatsby 最典型的开发模式。

完整代码

1. gatsby-config.js

module.exports = {
  siteMetadata: {
    title: '毕设作品集',
    description: '展示我的毕业设计项目',
    author: '张三',
  },
  plugins: [
    'gatsby-plugin-image',
    'gatsby-plugin-sharp',
    {
      resolve: 'gatsby-source-filesystem',
      options: { name: 'projects', path: `${__dirname}/src/data/` },
    },
    'gatsby-transformer-json',
  ],
};

2. src/data/projects.json

[
  {
    "name": "校园3D导览",
    "tech": "Three.js + Vue",
    "description": "基于WebGL的校园建筑3D展示与导航系统",
    "stars": 5
  },
  {
    "name": "智能课表助手",
    "tech": "React + Node.js",
    "description": "自动排课冲突检测与可视化课程表",
    "stars": 4
  },
  {
    "name": "宿舍报修平台",
    "tech": "Angular + Spring Boot",
    "description": "在线报修工单管理与进度跟踪系统",
    "stars": 4
  }
]

3. src/pages/index.js

import * as React from 'react';
import { graphql } from 'gatsby';

const IndexPage = ({ data }) => {
  const { title, description, author } = data.site.siteMetadata;
  const projects = data.allProjectsJson.nodes;

  return (
    <main style={{ fontFamily: 'system-ui', maxWidth: 700, margin: '0 auto', padding: 20 }}>
      <header style={{ textAlign: 'center', padding: '40px 0' }}>
        <h1>{title}</h1>
        <p style={{ color: '#666' }}>{description}</p>
        <small>by {author}</small>
      </header>

      <h2>📂 项目列表 ({projects.length})</h2>
      <div style={{ display: 'grid', gap: 16 }}>
        {projects.map((p, i) => (
          <div
            key={i}
            style={{
              background: 'white',
              padding: 20,
              borderRadius: 12,
              boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
              borderLeft: `4px solid ${p.stars >= 5 ? '#f59e0b' : '#3b82f6'}`,
            }}
          >
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <h3 style={{ margin: 0 }}>{p.name}</h3>
              <span>{'⭐'.repeat(p.stars)}</span>
            </div>
            <p style={{ color: '#666', fontSize: '0.9em' }}>{p.tech}</p>
            <p>{p.description}</p>
          </div>
        ))}
      </div>
    </main>
  );
};

// GraphQL 页面查询
export const query = graphql`
  query IndexPageQuery {
    site {
      siteMetadata {
        title
        description
        author
      }
    }
    allProjectsJson {
      nodes {
        name
        tech
        description
        stars
      }
    }
  }
`;

export default IndexPage;

export const Head = () => <title>毕设作品集</title>;

运行步骤

npm install -g gatsby-cli
gatsby new gatsby-demo
cd gatsby-demo
npm install gatsby-transformer-json gatsby-source-filesystem
# 替换上面文件
gatsby develop

预期输出

  • 页面通过 GraphQL 查询 siteMetadata 获取标题
  • 从 projects.json 读取项目列表渲染
  • 5 星项目左边框为金色
  • 访问 http://localhost:8000/___graphql 可交互式调试 GraphQL 查询

教程

Gatsby 入门教程:从数据到静态站点

一、Gatsby 是什么?

Gatsby 是一个基于 React 的静态站点生成器(SSG)。它的核心创新是 GraphQL 数据层——在构建时从任何来源(Markdown、CMS、API、数据库)拉取数据,生成纯静态 HTML/CSS/JS。曾是 JAMStack 运动的标杆,虽然近年被 Next.js 和 Astro 分流,但在内容密集型网站(文档站、营销站、博客)领域仍有大量用户。

核心概念

构建阶段:
  [数据源] → GraphQL 数据层 → React 组件 → 静态 HTML 页面
  (CMS/MDX/API)    (统一查询)        (模板渲染)       (输出到 public/)
  • Source Plugin:从数据源拉取数据到 GraphQL 层(如 gatsby-source-filesystem
  • Transformer Plugin:转换数据格式(如 gatsby-plugin-mdx 将 MDX 转为可查询节点)
  • Template:为每种数据类型指定渲染模板
  • gatsby-node.js:编程式创建页面

二、数据层详解

GraphQL 查询模式

http://localhost:8000/___graphql 可以交互式探索所有可用数据:

# 页面组件中用这种查询
query {
  site {
    siteMetadata { title }
  }
  allFile {
    nodes { name, extension }
  }
  allMdx {
    nodes {
      frontmatter { title, date }
      excerpt
    }
  }
}

# 非页面组件用 useStaticQuery hook
const data = useStaticQuery(graphql`
  query {
    site { siteMetadata { title } }
  }
`);

三、编程式创建页面

gatsby-node.js 中:

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  const result = await graphql(`
    query {
      allMdx {
        nodes {
          id
          fields { slug }
        }
      }
    }
  `);

  // 为每篇文章创建一个页面
  result.data.allMdx.nodes.forEach((node) => {
    createPage({
      path: node.fields.slug,
      component: path.resolve("./src/templates/blog-post.js"),
      context: { id: node.id }, // 传给模板的变量
    });
  });
};

// 为 Markdown 文件自动生成 slug
exports.onCreateNode = ({ node, actions }) => {
  const { createNodeField } = actions;
  if (node.internal.type === "Mdx") {
    const slug = "/posts/" + node.frontmatter.title
      .toLowerCase().replace(/\s+/g, "-");
    createNodeField({ node, name: "slug", value: slug });
  }
};

四、图片优化

Gatsby 的图片处理是杀手级特性:

import { GatsbyImage, getImage } from "gatsby-plugin-image";

// 查询
export const query = graphql`
  query {
    file(relativePath: { eq: "hero.jpg" }) {
      childImageSharp {
        gatsbyImageData(width: 800, placeholder: BLURRED, formats: [AUTO, WEBP])
      }
    }
  }
`;

// 渲染
const Hero = ({ data }) => {
  const image = getImage(data.file);
  return <GatsbyImage image={image} alt="Hero" />;
};

特性:自动生成多尺寸、WebP 格式、模糊占位图、懒加载。

五、部署

Gatsby 与 Gatsby Cloud(现为 Netlify 收购)、NetlifyVercel 深度集成:

# 构建产物在 public/
gatsby build

# Netlify:直接关联 Git 仓库,自动构建
# Vercel:框架预设选 Gatsby

六、与 Next.js / Astro 的对比

特性 Gatsby Next.js Astro
渲染方式 SSG SSG + SSR + ISR SSG + SSR
数据层 GraphQL 自选 自选
学习曲线 中等(需学 GraphQL) 中等
生态 丰富(3000+ 插件) 极其丰富 快速增长

七、思考题

  1. GraphQL 数据层适合什么场景?什么时候反而增加复杂度?
  2. 如果博客有 10000 篇文章,gatsby build 是否会变慢?如何优化?
  3. Gatsby 的 createPages 和 Remix 的文件系统路由哪种更灵活?

Gatsby 数据层与页面生成入门

背景

Gatsby 曾是 JAMStack 的旗帜。虽然近年来 Astro 和 Next.js 抢了风头,但 Gatsby 的 GraphQL 数据层 + 插件生态仍然是内容驱动网站的优秀方案。毕设中如果你需要从多个数据源(CMS、Markdown、API)聚合内容并生成静态网站,Gatsby 是最成熟的方案。


核心概念

GraphQL 数据层

Gatsby 最独特的特性:所有数据都通过 GraphQL 统一访问。

数据源(Markdown、CMS、API、JSON、数据库)
        ↓
   Gatsby Source Plugins
        ↓
   GraphQL Data Layer(统一数据层)
        ↓
   React 页面组件(通过 graphql 查询)

插件系统

// gatsby-config.js
module.exports = {
  plugins: [
    'gatsby-plugin-image',           // 图片优化
    {
      resolve: 'gatsby-source-filesystem',
      options: { name: 'posts', path: './content/posts/' }
    },
    'gatsby-transformer-remark',     // Markdown → HTML
  ]
};

分步操作

第一步:创建项目

npm install -g gatsby-cli
gatsby new my-site
cd my-site
gatsby develop

第二步:探索数据层

访问 http://localhost:8000/___graphql,这是 Gatsby 的 GraphQL 浏览器:

query {
  site {
    siteMetadata {
      title
    }
  }
  allFile {
    nodes {
      name
      extension
    }
  }
}

第三步:页面查询

// src/pages/index.js
import { graphql } from 'gatsby';

export default function Home({ data }) {
  return <h1>{data.site.siteMetadata.title}</h1>;
}

export const query = graphql`
  query HomeQuery {
    site {
      siteMetadata { title }
    }
  }
`;

第四步:程序化创建页面

// gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;
  const result = await graphql(`
    query { allMarkdownRemark { nodes { frontmatter { slug } } } }
  `);

  result.data.allMarkdownRemark.nodes.forEach(node => {
    createPage({
      path: `/blog/${node.frontmatter.slug}`,
      component: require.resolve('./src/templates/blog-post.js'),
      context: { slug: node.frontmatter.slug },
    });
  });
};

毕设推荐场景

个人技术博客

Markdown 写作 + Gatsby 生成 = 部署到 GitHub Pages。

作品集网站

JSON/YAML 管理项目数据,GraphQL 查询渲染。

多数据源聚合

同时展示 GitHub API + Markdown + CMS 内容。


思考题

  1. GraphQL 数据层相比直接 fetch API 有什么优势?
  2. gatsby-node.js 中的 createPages 在构建时还是运行时执行?
  3. Gatsby 的插件系统设计对开发效率有什么影响?

小结

Gatsby 的 GraphQL 数据层是它区别于其他 SSG 的核心特色。虽然学习曲线略高,但对多数据源聚合场景,它是最优雅的方案。