边写边学系列(四)—— Gatsby 搭建炫酷屌炸天的个人博客

3,951 阅读16分钟

前言

说来巧得不行,前两天正好买了本《唐诗三百首》附赠了一个电子书《了不起的盖茨比》,我心想现在淘宝卖书的都开始送编程书了咋呢?😄,结果人家是本小说,虽然我没读过,但是好像还很有名。不过没关系,作为一名前端 Coder,没读过《了不起的盖茨比》不要紧,但是 《静态站领域 NB 的 Gatsby》还是应该了解一下的。

首先,我得承认有点标题党了,因为这个博客并不能算是炫酷屌炸天,但是确实是花了很多心思,从配色到布局到动画效果到适配,都还挺用心的;

其次,如果又觉得不错的,我也开源出来了,博客架子源码地址:Gatsby-Animate-Blog;

最后,我想说一下为啥选择 Gatsby,有两点原因。第一,边写边学系列,我确实没用过 Gatsby,但是作为一个 Reacter,它的大名还是如雷贯耳的,个人比较擅长使用 Next.js,其实博客完全可以使用 Next.js 搭建,不过 React.js 官网推荐静态站的开发使用 Gatsby,一定有它的道理,因此,作为开发者的门面 —— 个人博客,我决定尝试使用 Gatsby 来进行开发。因为是边写边学,所以,里面会尽可能多的使用 Gatsby 特性来进行编写。

博客效果图:

博客地址

原本是打算放在自己域名上的,结果备案没通过,我也很无奈😭,暂时先用 now 提供的域名吧。

Gatsby 简介

就像上面说的,初识 Gatsby 我就是从 React 官网了解到的:

官方正式介绍的三个快速搭建 React.js 的开源框架/脚手架:第一个,大名鼎鼎的 CRA(create-react-app);第二个,最流行的 React SSR 框架 —— Next.js;第三个,就是今天的主角,React Static Websites 的王者 —— Gatsby。

术业有专攻,既然官方一直标榜它转为静态展开发而生,那么今天就一起来看看它在静态站领域到底有多牛X~

First Project

  • 安装
npm install -g gatsby-cli
  • 第一个项目
// 构建项目
# gatsby new gatsby-first-project
// 启动
# cd gatsby-first-project && yarn develop

然后浏览器访问http://localhost:8000就可以看到项目启动,而 Gatsby 还为我们额外提供了一个http://localhost:8000/__graphql查询界面。

目录结构

------
  | -- .cache
  | -- public
  | -- src
  | -- gatsby-browser.js    // 客户端相关配置
  | -- gatsby-config.js     // 基本配置文件    
  | -- gatsby-node.js       // node 相关配置文件
  | -- gatsby-ssr.js        // 服务端渲染相关配置文件 
  | ...                     // 其他文件

除了基本的目录,就是路由页约定和四个配置文件比较有意义,其中,gatsby-config 基本每个项目都有的,而其他三个如果没有使用到就可以删除,文章最后会单独介绍这几个重要的配置文件。

路由页约定

这个概念跟 Next.js 基本一模一样,Gatsby 不需要我们配置路由相关内容,而是将pages文件夹下的 js 文件名称映射为路由。

比如pages目录内容如下:

// pages
------
  | -- index.js     // 对应路径 /
  | -- about.js     // 对应路径 /about
  | -- pageA.js     // 对应路径 /pageA
  ...

然后,Gatsby 还有一些默认的设置,比如一个404.js对应着 404 Not Found 页面。

html.js

// 啥都别说,先执行一下这行命令再说
cp .cache/default-html.js src/html.js

这个文件是用来干啥的,Gatsby 的 html 结构就是通过这个 js 模板文件进行渲染的,比如我们想要配置一些东西,或者提前加载一些脚本文件,就可以重写它。

我们在文件里新增一行<meta name="author" content="luffyZh" />,查看一下:

可以看到,重写的内容生效了,就这么简单,其他的就看大家自己项目需求了~

GraphQL

Gatsby 官方推荐使用 GraphQL 进行查询,并且也配套了很多对应的插件~并且还提供了可视化查询页面。我们运行项目后,web 端运行在8000端口的同时,http://localhost:8000/___graphql地址运行着 GraphiQL 服务,具体的我懂的也不是特别多,大家可以自行观看~

比如,我们看看 gatsby-config.js 里面的内容:

module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `data`,
        path: `${__dirname}/src/data`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.dev/offline
    // `gatsby-plugin-offline`,
  ],
}

其中配置了站点数据,以及各种插件的配置,然后我们在 GraphiQL 服务里进行查询:

可以看到,结果出来了,并且这还是动态更新的,比如我们新添加一个 learnerData,再进行查询:

可以看到,也能查出来,当然,左侧列表中所有的内容都是可以进行查询的,里面包括整个项目各种资源配置详细信息,具体的大家可以去看官方文档说明,这里就不多赘述了,只讲一点,在这里查出来或者数据放在这里肯定是要用的,那么应该怎么用呢?下面就来介绍,如何在组件里获取对应数据。

Query Data

我们使用如下方式在页面进行数据的获取,使用内置的 graphql 查询 API,在页面组件定义查询,之后查询结果会通过属性data传递给组件,在组件就可以使用该值,具体如下:

import React from 'react';
import { graphql } from 'gatsby'; // 这个是数据获取的核心

export const query = graphql`
  query PageData {
    site {
      siteMetadata {
        title
        description
        author
      }
    }
  }
`;

const QueryPage = ({ data }) => {
  const { site: { siteMetadata, learnerData } } = data;
  return <div>
    <h1>siteMetaData:</h1>
    <p>title: {siteMetadata.title}</p>
    <p>description: {siteMetadata.description}</p>
    <p>author: {siteMetadata.author}</p>
  </div>
};

export default QueryPage;

注意,这种方式查询的 data,只有在组件内部才能进行对象取值操作,而在外部就是一个对应 id。

StaticQuery

与上面不同,上面是将查询语句与组件分离的形式进行操作,而 StaticQuery 是 Gatsby 为我们封装的组件,集数据查询与获取,使用如下:

import React from 'react';
import { StaticQuery, graphql } from 'gatsby';

const StaticQueryPage = () => (
  <StaticQuery
    query={graphql`
      query StaticQueryPageData {
        site {
          siteMetadata {
            title
            description
            author
          }
        }
      }
    `}
    render={
      data => {
        const { site: { siteMetadata, learnerData } } = data;
        return <div>
          <h1>StaticQueryData</h1>
          <h2>siteMetaData:</h2>
          <p>title: {siteMetadata.title}</p>
          <p>description: {siteMetadata.description}</p>
          <p>author: {siteMetadata.author}</p>
        </div>
      }
    }
  />
);

export default StaticQueryPage;

useStaticQuery

React 自从出了 Hooks 以后,很多人趋之若鹜,都慢慢的喜欢上了,很多插件也都配套出了 useXXX hook 的 API,这里也是一样,除了上面两种方法,在页面里你还可以使用useStaticQuery来进行查询,使用方式如下:

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

export default () => {
  const data = useStaticQuery(graphql`
    query UseStaticQueryData {
      site {
        siteMetadata {
          title,
          description,
          author
        }
      }
    }
  `);
  const { site: { siteMetadata, learnerData } } = data;
  return (
    <div>
      <h1>UseStaticQueryData</h1>
      <h2>siteMetaData:</h2>
      <p>title: {siteMetadata.title}</p>
      <p>description: {siteMetadata.description}</p>
      <p>author: {siteMetadata.author}</p>
    </div>
  )
}

更多

这里就只介绍上面几个页面组件级别的,如果是纯前端基本够用了,不过 Gatsby 还有更多高级用法,比如 node 端配置页面等等。关于更多内容或者 Gatsby GraphQL 相关,Gatsby 专门有一个章节,叫做 why-gatsby-uses-graphql,大家可以阅读一下。

API && 插件

简单使用下来发现,Gatsby 所谓的便捷即使因为他将原本手动做的或者自己组装的各种需求轮子都在自己的社区搞成了插件,庞大的插件系统,即插即用是它便捷的原因,因此,就来了解一下它的一些 API 以及几个重要的插件。

Link

第一个,基本所有框架都必备的,路由部分。Gatsby 的 Link 跟一般的前端路由基本一致,没有太大区别,因为它只是对reach/router进行了包装。。

<Link to='/pageA'>Link to pageA</Link> // 前端路由
<a href='/pageA'>A tag to pageA</a>    // a 标签跳转

当然,既然做了封装,Gatsby 的 Link 组件一定有它的独到之处,比如:activeClassNameactiveStyle,顾名思义,就是选中时的状态,如果是一般框架,这个选中状态可能需要我们用额外的代码去控制,在 Gatsby 使用这两个类就可以了。我们用下段代码看看效果:

// layout.css
.active {
    color: red
}

// nav.js
    <Link
      to="/"
      style={{ color: '#fff' }}
      activeClassName="active"
    >
      Home
    </Link>
    /
    <Link
      to="/about/"
      style={{ color: '#fff' }}
      activeStyle={{ color: "#000", background: '#fff' }}
    >
      About
    </Link>

哎呀,发现activeClassName并不管用,为啥呢?仔细看了一下,原来 Link 组件使用了 style 约定了 color 样式,style 优先级要高,那么有没有其他办法呢?有,我们看下段代码:

<Link
  to="/"
  getProps={({ isCurrent }) => {
    // the object returned here is passed to the
    // anchor element's props
    return {
      style: {
        color: isCurrent ? "red" : "#fff"
      }
    };
  }}
>
  Home
</Link>

嗯,关于 Link 还有更多好用的功能,就不一一介绍了,可以去官方文档去看 -> Gatsby Link

gatsby-source-filesystem

这个插件是 gatsby 被引用最多的插件,一般与其他插件配合使用,比如 GraphQL 和 gatsby-node,用于设置项目的文件系统,并且还可以设置多个。

 {
   resolve: `gatsby-source-filesystem`,
   options: {
     name: `images`,
     path: `${__dirname}/src/images`,
   },
   {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `data`,
        path: `${__dirname}/src/data`,
      },
   },
 }

比如,上面的代码,我设置了两个静态文件系统,一个是 images,一个是 data,我们可以通过 GraphiQL 来进行查询:

既然都可以查询出来了,也就表示我们可以在代码里使用他们。下面就简单的写个页面列出来所有的文系统静态资源文件。

import React, { Fragment } from 'react';
import { graphql, useStaticQuery } from 'gatsby';

const List = () => {
  const data = useStaticQuery(
    graphql`
      query ListQuery {
        allFile {
          edges {
            node {
              id
              name
              relativePath
              publicURL
              dir
            }
          }
        }
      }
    `
  );
  const { allFile: { edges } } = data;
  return (
    <table border='1'>
      <thead>
        <tr>
          <th>文件名</th>
          <th>所属目录</th>
          <th>预览地址</th>
        </tr>
      </thead>
      <tbody>
         渲染文件列表
      </tbody>
    </table>
  )
}

export default List;

可以看到,确实很方便,正常来说这都是需要后台进行大量的代码工作的,而 Gatsby 只用一个简易的插件配置再搭配上 GraphQL 语法查询就可以了,真香~其他关于插件更多可以去官网插件库查看,该插件还有其他高级功能,并且该插件也是很多插件的依赖~

Static

Gatsby 支持我们设置静态文件引入,新建static文件夹,里面的文件会对应/filename获取,该文件夹在编译时会拷贝一份到public文件夹里,同时看到上面代码的应该也发现了,gatsby-source-filesystem这个插件也会将文件夹下面的文件存放一份到/static文件夹里我们可以通过publicURL字段使用 GraphQL 进行查询。

import React from 'react';

const Image = () => (
  <div>
    <img alt='image' src='/gatsby-astronaut.png' />
  </div>
);

export default Image;

Gatsby-Image

Gatsby 官方自己说的:Gatsby 网站之所以快的部分原因,很大程度上在于图像的处理方法。Gatsby 利用的是插件gatsby-plugin-sharp和 GraphQL 配合的模式来完成图像的加载。

官方特意强调了,gatsby-image并不能代替<img/>标签,具体使用还是得看场合。

Gatsby-Image 需要几个官方插件一起使用,而本地图片的加载依靠的就是上面介绍的gatsby-source-filesystem

yarn add gatsby-image gatsby-plugin-sharp gatsby-transformer-sharp gatsby-source-filesystem

接下来就看看相关优化的插件要怎么用:

// gatsby-config.js

module.exports = {
  plugins: [
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: 'images',
        path: `${__dirname}/src/images/`,
      },
    },
  ],
}
  • gatsby-image

gatsby-image解决了大型网站构建时的如下问题:

1. 将大图像调整为设计所需的大小

2. 生成多个较小的图像,以便智能手机和平板电脑不会下载桌面尺寸的图像

3. 剥离所有不必要的元数据并优化 JPEG 和 PNG 压缩

4. 高效延迟加载图像以加快初始页面加载速度并节省带宽

5. 使用“模糊处理”技术或“跟踪的占位符” SVG 在加载图像时显示图像预览

6. 保持图片位置,以便在加载图片时您的页面不会错乱
  • gatsby-plugin-sharp && gatsby-transformer-sharp

这两个插件都是 gatsby 基于 sharp 图像处理工具进行扩展的。具体使用就是简单的在加载图片的时候进行宽高以及质量进行设置。

上面都稀里糊涂的安装完了,我们来看看这些插件都做了什么:

事先声明一下,因为 Gatsby 的内置约束插件系统太多了,所以文章可能充斥着很多说明性图片,没办法,我自己并不能用语言解释清楚,只能图片来说了。

可以看到,左侧查询列表里出现了两个带有 imageSharp 关键字的字段,他们就是上面两个 sharp 插件帮我们做的一些关于图片的一些优化。 比如我们查询一下所有的本地优化图片:

嗯,这里有两个关键点。

  • 第一,这里面的图片就是我们通过gatsby-source-filesystem配置的文件夹里面的所有图片,注意,只有配置了才可以。

/static文件夹下面有三个图片,配置的/src/images文件夹下有两个图片,能查到的只有images文件夹下的两张图片。

  • 第二,经过xxx-sharp插件处理过的图片是通过 GraphQL 方法创建的, 可用的图像优化有两种类型,固定图像 - fixed 和流体图像 - fluid。

那么这两种类型图片有什么区别呢?简单点,fixed 返回的是固定尺寸的图片,而 fluid 返回的是流式图片,它会根据容器宽度进行自适应。我觉得还是直接来代码实践吧:

// /page/image.js
import React from 'react';
import { useStaticQuery, graphql } from "gatsby";
import iconImage from '../images/gatsby-icon.png'; 
import Img from 'gatsby-image';

const Image = () => {
  const data = useStaticQuery(graphql`
    query {
      fixedIconImage: file(relativePath: { eq: "gatsby-icon.png" }) {
        childImageSharp {
          fixed(width: 200) {
            ...GatsbyImageSharpFixed
          }
        }
      }
      fluidIconImage: file(relativePath: { eq: "gatsby-icon.png" }) {
        childImageSharp {
          fluid {
            ...GatsbyImageSharpFluid
          }
        }
      }
    }
  `)
  return (
    <div>
      <h1>静态引入文件</h1>
      <img alt='static-image' src='/gatsby-icon.png' />
      <h1>import 引入文件</h1>
      <img alt='import-image' src={iconImage} />
      <h1>fixed 文件</h1>
      <Img alt='fixed-image' fixed={data.fixedIconImage.childImageSharp.fixed} />
      <h1>fluid 文件</h1>
      <Img alt='fluid-image' fluid={data.fluidIconImage.childImageSharp.fluid} />
    </div>
  );
}

export default Image;

最高级的功能,先来看看如何使用,需要通过引入gatsby-image插件,并且参数和正常的<img />标签也不一样,它不接收 src 属性,而是接收对应的 fixed 或 fluid 属性,该属性就是通过 GraphQL 查询出来的:

嗯,看到了吧,fluid 类型的文件会撑满容器宽度。简要的关键点说完了,接下来得了解一下 Gatsby-Image 吹了这么半天,到底做了哪些优化,还是用两张图片来说明一下:

第一张图片,以 fluid 为例,查询图片的 src,第二张图片是编译过的项目会将配置过后的图片资源生成多种分辨率不同形式(src/base64...),并且在实际运行中按需加载最优的方案。当然,这个最优是 Gatsby 帮我们判断的。

上面这张图说得很清楚了也很有说服力,gatsby-image通过<picture>标签来加载图片,里面有<source>标签,里面放置的是链接地址也就是上面我们截图的内容。而下方用来显示图片的就是<img>标签,它会根据不同设备不同分辨率进行图片的优化加载,并且图片的加载方式是懒加载loading='lazy'

gatsby-transformer-remark

上面几个基本是 Gatsby 必用插件,讲完也就差不多了,这里因为是要搭建博客站,因此额外再介绍一个插件,因为下面也会用到,怕大家不知道就在这里介绍了。这个插件很简单,就是解析项目里的.md文件,然后我们就可以通过 graphql 进行查询了,这样也便于进行文章内容的动态配置生成。

// In your gatsby-config.js
plugins: [
  {
    resolve: `gatsby-transformer-remark`,
    options: {
      // CommonMark mode (default: true)
      commonmark: true,
      // Footnotes mode (default: true)
      footnotes: true,
      // Pedantic mode (default: true)
      pedantic: true,
      // GitHub Flavored Markdown mode (default: true)
      gfm: true,
      // Plugins configs
      plugins: [],
    },
  },
],

使用方式就是配置进去就行,然后我们就会发现,Graphiql 多了两个字段:

上面我们项目里新增了/posts文件夹,里面存放了两个.md文件,正好被查询出来了。除此之外,还可以为 Markdown 文件配置相关头部内容供使用,具体约定的格式如下:

// posts/first.md

---
title: "第一篇文章"
author: "luffyZh"
tag: "React,HTML"
date: "2020-01-12"
---

## 我是第一篇文章

> 第一篇文章

通过插件,就会解析头部内容为frontmatter字段,里面是我们配置好的字段属性。

Gatsby 配置文件介绍

gatsby-config.js

这个文件很好说了,任何框架任何项目都有自己的配置文件,而gatsby-config.js就是整个 Gatsby 站点的配置文件。我们可以在里面配置网站的相关基本信息:

  • siteMetadata (object) - 站点基本信息
module.exports = {
 siteMetadata: {
   title: `Gatsby`,
   siteUrl: `https://www.gatsbyjs.org`,
   description: `Blazing fast modern site generator for React`,
 },
}
  • plugins (array) - 插件配置
module.exports = {
  plugins: [
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `docs`,
        path: `${__dirname}/../docs/`,
      },
    },
  ],
}
  • pathPrefix (string) - 可以配置网站路径前缀
module.exports = {
  pathPrefix: `/blog`,
}

这个配置场景也很有用,那就是 Github Pages 部署项目的时候的路径问题,更多详细可以去看文档

  • polyfill (boolean) - polyfill 相关

  • mapping (object) - 比较高级,不知道怎么用。。。😄

  • proxy (object) - 代理

module.exports = {
  proxy: [
    {
      prefix: "/api",
      url: "http://dev-mysite.com",
    },
    {
      prefix: "/api2",
      url: "http://dev2-mysite.com",
    },
  ],
}

更多查看文档

  • developMiddleware (function) 开发中间件,类似于 redux/node 中间件系统,这里我也没用过,所以不过多介绍。

gatsby-browser.js

一些浏览器相关的 API 通过在这个文件里去实现,比如一些监听路由变化,注册 serviceWorker 等等。

  • 监听路由变化
// 监听路由变化
exports.onRouteUpdate = ({ location, prevLocation }) => {
  console.log('new pathname', location.pathname)
  console.log('old pathname', prevLocation ? prevLocation.pathname : null)
};

  • wrapPageComponent - 全局Layout 包裹页面组件,在前面代码里看到了,我们有一个 Layout 组件,但是如果要使用,因为我们每一个页面对应一个js,也就是每一个页面都要引入一下才行,这不是明智之举,一点都不优雅,诸如 Next.js 解决方案是 _app.js。Gatsby 也有解决方案,就是 gatsby-browser.js 的 wrapPageComponent。
// 因为是 Node.js,因此引入方式不是 import 
const React = require("react");
const Layout = require("./src/components/layout").default;

exports.wrapPageElement = ({ element, props }) => {
  // props provide same data to Layout as Page element will get
  // including location, data, etc - you don't need to pass it
  return <Layout {...props}>{element}</Layout>
}

可以看到,已经将我们所有页面都使用 Layout 组件包裹上了。里面出现重复是因为我没有把原来代码里的 Layout 删除掉,因为只是使用方法,这么看更直观。

  • wrapRootComponent - Provider 相关

这个跟上面组件是一样的场景,只不过是用来包装根组件的,比如 redux 的 Provider,比如 ant-design 的国际化等。

// 引入 redux
const React = require("react")
const { Provider } = require("react-redux")

const createStore = require("./src/state/createStore")
const store = createStore()

exports.wrapRootElement = ({ element }) => {
  return (
    <Provider store={store}>
      {element}
    </Provider>
  )
}

具体更多的详见browser-api

gatsby-node.js

这个配置文件里面的代码顾名思义是 node 层相关的,因此它只会在 build 期间运行一次,比如动态创建一些页面。这里有个场景可以想一下,一般 Gatsby 用来做静态站,我们熟知的也就是个人博客,那么庞大的文章列表难道要手动维护?肯定不合理,因此就适合在 node 层动态生成文章列表文章内容页等等。

前面说过了,Gatsby会把src/pages目录下的文件全部渲染成路由,这是一个约定,但是难免会这么一种场景,就是页面是带参数动态生成的,这种时候pages的定义方式就捉襟见肘了,此时就需要利用gatsby-node.js来进行配置了。先来简单写一个后端创建页面的示例:

// gatsby-node.js

const path = require('path');

exports.createPages = ({ actions }) => {
  const { createPage } = actions;
  const staticTemplate = path.resolve(`src/templates/static.js`)
  // Query for markdown nodes to use in creating pages.
  // You can query for whatever data you want to create pages for e.g.
  // products, portfolio items, landing pages, etc.
  // Variables can be added as the second function parameter
  createPage({
    // Path for this page — required
    path: `node-static`,
    component: staticTemplate,
    context: {
      title: '我是静态页面',
      content: '静态页内容'
    },
  });
};

上面代码在 gatsby-node.js 的 createPages API 创建一个路由,模板是/src/templates/static.js,对应路径是/node-static,而创建页面以及传递属性使用的是actions.createPage,它的context属性可以配置一些属性作为 props 传递给组件,而在组件内部我们可以通过几种方式来获取:

// 第一种 - 通过 pageContext 获取 context
import React from 'react';

export default (props) => {
  const { pageContext: { title, content } } = props;
  return (
    <>
      <h1>{title}</h1>
      <p>{content}</p>
    </>
  )
};

// 第二种 - 通过 GraphQL 获取
export const data = graphql`
  query {
    sitePage(path: {eq: "/node-static"}) {
      id
      context {
        title
        content
      }
    }
  }
`;

const { sitePage: { context: { title, content } } } = data;

上面只是一个简单的演示 node 层如何创建页面,不过呢,如果是这种页面,肯定是放在src/pages目录下更合理,通过 node 创建的页面一定是更复杂的页面。我说过了想使用 Gatsby 搭建一个个人博客,因此就使用 gatsby-node.js 来动态创建博客文章页吧。

上图,我新建了一个 posts 文件夹,里面存放博客文章(.md 文件)。在 gatsby-node.js 编写代码动态生成每个文章的页面。

// gatsby-node.js
exports.createPages = ({ actions, graphql }) => {
  const { createPage } = actions;  
  ...
  /* 创建一个复杂的动态页面 */
  const blogPostTemplate = path.resolve(`src/templates/blog-post.js`)
  return graphql(`
    query {
      allMarkdownRemark {
        edges {
          node {
            id,
            html
          }
        }
      }
    }
  `).then(result => {
    if (result.errors) {
      throw result.errors
    }

    // Create blog post pages.
    result.data.allMarkdownRemark.edges.forEach(edge => {
      createPage({
        // Path for this page — required
        path: `posts/${edge.node.id}`,
        component: blogPostTemplate,
        context: {
          html: edge.node.html
        },
      })
    })
  });
  ...
}

这里是用了 graphql 查询,注意这个graphql来自 createPages 的 API,我们查询到所有的 markdown 文章内容,然后逐个遍历生成路由页面,定义是 posts/${articleId},重启服务访问一下:

可以看到,两篇文章在 graphql 是可以查询到的,并且我们前端组件也正常渲染了对应文章页面。这只是牛刀小试,还有很多更方便的功能,具体更多的详见browser-api

gatsby-ssr.js

这个文件,看名字意思是服务端渲染相关处理,给的示例是这样的,你可以将服务端渲染的 HTML 代码在此文件里进行二次加工处理。

// gatsby-ssr.js 

const React = require("react")
// 给 body 标签添加一个类名字为 my-body-class
exports.onRenderBody = ({ setBodyAttributes }, pluginOptions) => {
  setBodyAttributes({
    className: "my-body-class",
  })
};

但是这里需要注意一点,wrapPageElementwrapRootElement这两个 API 是 gatsby-browser.js 和 gatsby-ssr.js 共享的。最好保证只在其中一个文件使用它们,不然可能会出现渲染不一致的场景。

其余还有很多 API,不过 gatsby-ssr.js 更多的是与/src/html.js文件一起讨论,它修改的内容会覆盖html.js的内容。

具体更多的详见browser-api

部署

关于部署,因为我是 Next.js 爱好者,因此这里选择使用 now 来进行部署,当然也有其他的方式,比如 surge。

now

嗯,完事了,当然,前提是你安装了now-cli,具体的可以去 now 官网去安装,反正安装完你直接运行命令 now 就完事了~

我们来看看部署后的网站:

总结

一篇文章想把 Gatsby 所有内容尽可能的讲到确实太难了,文章架构组织了好久,最后决定是上面那样,可能不能算完美,但是我觉得已经是新手能够接受的循序渐进了。因为 Gatsby 技术耦合很严重,你如果想使用它,就必须前置掌握太多的东西了(node/react/graphql...)。

上面基本把我用到的学到的通过自己的理解都解释给大家了,Gatsby 使用下来发现自己真的挺无力,哈哈,为啥呢?因为你能做的只是写页面代码,其他的就是按照 Gatsby 的约束安装插件直接使用就行,怎么说呢?写下来毫无成就感~当然了也侧面印证了 Gatsby 确实适合快速建站,啥都不需要你管你就可以有一个各方面都比较完美的静态站,因为他将很多性能优化点都集成在了项目以及插件系统,我们拿来主义直接使用就 OK 了。

学习项目地址

开源博客地址