Gatsby + Ant desgin + TypeScript + GraphQL 开发静态博客

2,212 阅读3分钟

使用Gatsby + Ant desgin + TypeScript + GraphQL 开发静态博客系统

为什么选择Gatsby

  • 开箱即用
  • 纯静态站点访问快
  • 使用React + GraphQL + Pwa 等新技术
  • 部署简单
  • 插件丰富

Gatsby开发流程

安装流程

1.安装工具 Gatsby CLI

npm install -g gatsby-cli

2.查看版本

gatsby --version

3.创建项目

gatsby new project-name

4.启动项目

gatsby develop or npm start

5.访问项目

项目主页 http://localhost:8000/

GraphiQL 查询界面 http://localhost:8000/___graphql

项目目录说明

├── json               # 项目数据
│   └── markdown       # markdown文档
│       └── html       # 文档分类
├── public             # build之后的项目目录
└── src                # 资源文件夹
    ├── components     # 组件
    ├── images         # 图片
    ├── less           # 样式文件
    ├── pages          # 页面
    ├── templates      # 模板
    └── types          # typescript类型

支持less

在 gatsby 应用中支持 less,需要依赖gatsby-plugin-less,只需要下载之后在plugins加入这个插件就可以支持了。

1.安装插件:

npm install --save gatsby-plugin-less

2.插件配置:

gatsby-config.js

plugins: [
  `gatsby-plugin-less`
  ...
]

3.使用less

src/components/Layout.tsx 文件中

import "../less/layout.less"

src/less/layout.less

// 页面主要布局
.layout{
  width:100%;
  overflow-y: scroll;
}

布局

  1. 引入Ant desgin
npm install antd

2.配置主题

gatsby-config.js配置ant 主题

  plugins: [
    {
      resolve: `gatsby-plugin-less`,
      options: {
        lessOptions: {
          modifyVars: {
            'primary-color': '#1DA57A',
            'link-color': '#1DA57A',
          },
          javascriptEnabled: true,
        },
      },
    },
  ]

3.创建布局文件

src/components/ 创建 Layout.tsx

/**
 * Layout component that queries for data
 * with Gatsby's useStaticQuery component
 *
 * See: https://www.gatsbyjs.com/docs/use-static-query/
 */

import * as React from "react"
import PropTypes from "prop-types"
import { Link, useStaticQuery, graphql } from "gatsby"
import { StaticImage } from "gatsby-plugin-image"
import "../less/layout.less"
import { Layout, Menu, Space } from 'antd'
const { Header, Footer, Content } = Layout

const LayoutDefault: React.FC = ({ children }) => {
  const data = useStaticQuery(graphql`
    query SiteTitleQuery {
      site {
        siteMetadata {
          title
        }
      }
    }
  `)

  return (
    <Layout className="layout">
      <Header className="header">
        <Link to="/" className="logo">
          <StaticImage
            src="../images/3pixel_logo.png"
            width={110}
            formats={["auto", "webp", "avif"]}
            alt={data.site.siteMetadata?.title || `Title`}
          />
        </Link>
        <Menu
          className="menu"
          theme="dark"
          mode="horizontal">
          <Menu.Item key="1">
            <Link to="/" activeClassName="cur">首页</Link>
          </Menu.Item>
          <Menu.Item key="2">
            <Link to="/html" activeClassName="cur">Html</Link>
          </Menu.Item>
        </Menu>
      </Header>
      <Content className="content">
        {children}
      </Content>
      <Footer className="footer">
        <Space direction="vertical">
          <div>© {new Date().getFullYear()} <a href="https://beian.miit.gov.cn">陕ICP备14011119号-2</a></div>
          <Space>
            博客支持:
            <a href="https://www.gatsbyjs.com">Gatsby</a>
            <a href="https://react.docschina.org/">React</a>
            <a href="https://ant.design/index-cn">Ant Desgin</a>
            <a href="https://www.tslang.cn/">TypeScript</a>
          </Space>
        </Space>
      </Footer>
    </Layout>
  )
}

LayoutDefault.propTypes = {
  children: PropTypes.node.isRequired,
}

export default LayoutDefault

4.使用布局

src/pages 下创建 404.tsx 如下:

import * as React from "react"

import Layout from "../components/Layout"
import FixedContent from "../components/FixedContent"
import Seo from "../components/Seo"
import { PageProps, Link } from "gatsby"
import { Result, Button } from 'antd'

const NotFoundPage: React.FC<PageProps> = () => (
  <Layout>
    <Seo title="404: Not found" />
    <FixedContent>
      <Result
        status="404"
        title="404"
        subTitle="对不起页面没有找到"
        extra={
          <Link to="/">
            <Button type="primary">返回首页</Button>
          </Link>
        }
      />
    </FixedContent>
  </Layout>
)

export default NotFoundPage

添加MD文档

  1. json/markdown 下面创建markdown文件如: blog1.md
---
date: "2021-09-15"
title: "第一篇测试文章"
description: "第一篇测试文章摘要"
---

> 测试文章摘要

## 测试标题1

sdfsfsd

## 测试标题2

- 测试标题1
- 测试标题2

2.插件配置

安装 gatsby-source-filesystem 可检索提取项目中的Md和YML/YMAL等文件,注意要配置其path包含放MD文件的位置 安装 gatsby-transformer-remark 可对提取的MD类文件解析成json等格式的数据源

npm install gatsby-source-filesystem gatsby-transformer-remark

gatsby-config.js文件参考⤵️

  plugins: [
    {
      resolve: "gatsby-source-filesystem",
      options: {
        name: "data",
        path: `${__dirname}/json/markdown/`
      }
    },
    {
      resolve: 'gatsby-transformer-remark',
      options: {
        plugins: [
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 590,
              linkImagesToOriginal: false,
            },
          },
        ]
      }
    },
  ]

4.解析MD文件

src/templates 下创建 页面模板blog.tsx

import * as React from "react"
import Layout from "../components/Layout"
import Seo from "../components/Seo"
import { PageProps, graphql } from "gatsby"
import type { BlogDetail } from '../types/blog'
import FixedContent from "../components/FixedContent"
import PageSection from "../components/PageSection"
import { Breadcrumb } from 'antd';
 
type DataProps = {
  markdownRemark: BlogDetail
}

const Blog: React.FC<PageProps<DataProps>> = ({ data }) => {
  const post = data.markdownRemark
  return (
    <Layout>
      <Seo title="post" />
      <FixedContent>
        <Breadcrumb className="breadcrumb">
          <Breadcrumb.Item href="/">首页</Breadcrumb.Item>
          <Breadcrumb.Item>{post.frontmatter.title}</Breadcrumb.Item>
        </Breadcrumb>
        <PageSection>
          <h1>{post.frontmatter.title}</h1>
          <p>{post.frontmatter.date}</p>
          <p>{post.frontmatter.description}</p>
          <div dangerouslySetInnerHTML={{ __html: post.html }} />
        </PageSection>
      </FixedContent>
    </Layout>
  )
}

export default Blog

export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      id
      fields {
        slug
      }
      frontmatter {
        date(formatString: "YYYY-MM-DD", locale: "zh-CN")
        description
        title
      }
    }
  }
`

同时在gatsby-node.js文件中利用createPages加上页面解析

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.tsx`),
      context: {
        // Data passed to context is available
        // in page queries as GraphQL variables.
        slug: node.fields.slug,
      },
    })
  })
}

5.MD代码块高亮

增加gatsby-remark-prismjs插件

npm install gatsby-remark-prismjs

gatsby-config.js中配置插件

  plugins: [
    {
      resolve: "gatsby-transformer-remark",
      options: {
        plugins: [
          `gatsby-remark-images`,
          {
            resolve: `gatsby-remark-prismjs`,
            options: {
              classPrefix: "language-",
              inlineCodeMarker: null,
              aliases: {},
              showLineNumbers: false,
              noInlineHighlight: false,
              languageExtensions: [
                {
                  language: "superscript",
                  extend: "javascript",
                  definition: {
                    superscript_types: /(SuperType)/,
                  },
                  insertBefore: {
                    function: {
                      superscript_keywords: /(superif|superelse)/,
                    },
                  },
                },
              ],
              prompt: {
                user: "root",
                host: "localhost",
                global: false,
              },
              escapeEntities: {},
            },
          },
        ]
      }
    },
  ]

gatsby-browser.js 中增加样式

require("prismjs/themes/prism-solarizedlight.css")

GraphQL 数据

使用 http://localhost:8000/___graphql 查询所需数据

graphql1.png 1.在组件中使用GraphQL, 如: src/components/Layout.tsx

  const data = useStaticQuery(graphql`
    query SiteTitleQuery {
      site {
        siteMetadata {
          title
        }
      }
    }
  `)

2.在页面中使用GraphQL, 如: src/pages/index.tsx

export const query = graphql`
  query {
    allMarkdownRemark(sort: { order: DESC, fields: [frontmatter___date] }) {
      totalCount
      edges {
        node {
          id
          fields {
            slug
          }
          frontmatter {
            date(formatString: "YYYY-MM-DD", locale: "zh-CN")
            description
            title
          }
        }
      }
    }
  }
  `

Gatsby部署

  1. 使用gh-pages插件, 创建gh-pages分支, 只把编译后的同步到仓库

安装到本地

npm install gh-pages --save-dev

配置 package.json

"scripts": {
  "deploy": "gatsby build && gh-pages -d public -b master -r https://git.avenirzheng.net/blog.git"
},

2.部署到腾讯云轻服务器

创建域名: gatsby.blog.3pixellab.com

配置Nginx:

server
{
    listen 80;
    server_name gatsby.blog.3pixellab.com;
	  index index.html;
    root /www/wwwroot/gatsby.blog.3pixellab.com/;
    
    #SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
    #error_page 404/404.html;
    #SSL-END
    
    #ERROR-PAGE-START  错误页配置,可以注释、删除或修改
    #error_page 404 /404.html;
    #error_page 502 /502.html;
    #ERROR-PAGE-END
    
    #PHP-INFO-START  PHP引用配置,可以注释或修改
    include enable-php-00.conf;
    #PHP-INFO-END
    
    #REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
    include /www/server/panel/vhost/rewrite/gatsby.blog.3pixellab.com.conf;
    #REWRITE-END
    
    #禁止访问的文件或目录
    location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
    {
        return 404;
    }
    
    #一键申请SSL证书验证目录相关设置
    location ~ \.well-known{
        allow all;
    }
    
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
    {
        expires      30d;
        error_log /dev/null;
        access_log /dev/null;
    }
    
    location ~ .*\.(js|css)?$
    {
        expires      12h;
        error_log /dev/null;
        access_log /dev/null; 
    }
    access_log  /www/wwwlogs/gatsby.blog.3pixellab.com.log;
    error_log  /www/wwwlogs/gatsby.blog.3pixellab.com.error.log;
}

创建文件夹 gatsby.blog.3pixellab.com 并拉去代码, 切换到 'gh-pages' 分支

3.使用npm run deploy 就可以发布到生产环境了

参考文档

总结

项目地址: gitee.com/kermityu/ga…

这个博客项目让我全方位的了解了Gatsby, 并实际使用了GraphQL TypeScript 等新的技术, 让自己也有了更高的提升

目前遗留一个问题

  • 页面切换的时候,logo图片会闪烁