Gatsby静态站点生成器

一、Gatsby 介绍

Gatsby 是一个静态站点生成器

官网:https://www.gatsbyjs.org/

静态应用的优势

  1. 访问速度快
  2. 更利于 SEO 搜索引擎的内容抓取
  3. 部署简单

二、Gatsby工作流程与框架特性

Gatsby 总览

在这里插入图片描述

  1. 基于 React 和 GraphQL. 结合了 webpack, babel, react-router 等前端领域中最先进工具. 开发人员开发体验好
  2. 采用数据层和UI层分离而不失 SEO 的现代前端开发模式. 对SEO非常友好
  3. 数据预读取, 在浏览器空闲的时候预先读取链接对应的页面内容. 使静态页面拥有 SPA 应用的用户体验, 用户体验好
  4. 数据来源多样化: Headless CMS, markdown, API.
  5. 功能插件化, Gatsby 中提供了丰富且功能强大的各种类型的插件, 用什么装什么

三、创建 Gatsby 项目

  1. 全局安装脚手架工具
    npm install gatsby-cli -g

  2. 创建项目
    创建:gatsby new project-name https://github.com/gatsbyjs/gatsby-starter-hello-world
    启动:gatsby developnpm start
    访问:localhost:8000

四、基于文件的路由系统

Gatsby 框架内置基于文件的路由系统, 页面组件被放置在 src/pages 文件夹中

pages/list.js

在这里插入图片描述
访问:
在这里插入图片描述

五、以编程的方式创建页面

基于同一个模板创建多个HTML页面,有多少数据就创建多少页面
比如商品详情页面,有多少商品就生成多少商品详情展示页面.

在这里插入图片描述
gatsby-node.js

const path = require("path")

// 创建页面
function createPages({ actions }) {
  const { createPage } = actions
  // 获取模板的绝对路径
  const template = require.resolve("./src/templates/person.js")
  // 获取模板所需要的数据
  const persons = [
    { slug: "zhangsan", name: "张三", age: 20 },
    { slug: "lisi", name: "李四", age: 30 },
  ]
  // 根据模板和数据创建页面
  persons.forEach(person => {
    createPage({
      // 模板绝对路径
      component: template,
      // 访问地址
      path: `/person/${person.slug}`,
      // 传递给模板的数据
      context: person,
    })
  })
}

/src/templates/person.js

import React from "react"

export default function Person({ pageContext }) {
  const { name, age } = pageContext
  return (
    <div>
      <span>{name}</span>
      <span>{age}</span>
    </div>
  )
}

访问:
在这里插入图片描述

六、link组件的使用

在 Gatsby 框架中页面跳转通过 Link 组件实现

在这里插入图片描述

七、GraphQL 数据层

在 Gatsby 框架中提供了一个统一的存储数据的地方,叫做数据层.
在应用构建时,Gatsby 会从外部获取数据并将数据放入数据层,组件可以直接从数据层查询数据.
数据层使用 GraphQL 构建.
调试工具:localhost:8000/___graphql

在这里插入图片描述
在这里插入图片描述

八、GraphQL 数据查询

页面组件

在组件文件中导出查询命令, 框架执行查询并将结果传递给组件的 prop 对象. 存储在 props 对象的 data 属性中.

在这里插入图片描述
index.js

import React from "react"
import { Link, graphql } from "gatsby"
import SEO from "../components/SEO"
import styles from "../styles/index.module.less"

export default function Home({ data }) {
  return (
    <>
      <SEO title="index page" />
      <div>
        <Link className={styles.red} to="/person/zhangsan">
          zhangsan
        </Link>
        <Link to="/person/lisi">lisi</Link>
        <p>{data.site.siteMetadata.title}</p>
        <p>{data.site.siteMetadata.author}</p>
      </div>
    </>
  )
}

export const query = graphql`
  query MyQuery {
    site {
      siteMetadata {
        author
        title
      }
    }
  }
`

非页面组件

通过钩子函数 useStaticQuery 进行手动查询

在这里插入图片描述
header.js

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

export default function Header() {
  const data = useStaticQuery(graphql`
    query {
      site {
        siteMetadata {
          author
          title
        }
      }
    }
  `)
  return (
    <div>
      <p>{data.site.siteMetadata.title}</p>
      <p>{data.site.siteMetadata.author}</p>
    </div>
  )
}

九、Gatsby 插件

Gatsby 框架内置插件系统, 插件是为应用添加功能的最好的方式.
在 Gatsby 中有三种类型的插件: 分别为数据源插件 ( source ), 数据转换插件 ( transformer ), 功能插件 ( plugin )
数据源插件:负责从应用外部获取数据,将数据统一放在 Gatsby 的数据层中
数据转换插件:负责转换特定类型的数据的格式,比如将 markdown 文件中的内容转换为对象形式
功能插件:为应用提供功能,比如通过插件让应用支持 Less 或者 TypeScript.
https://www.gatsbyjs.org/plugins/

十、将 JSON 数据放入数据层

要将本地 JSON 文件中的数据放入数据层需要用到两个插件.
gatsby-source-filesystem: 用于将本地文件中的数据添加至数据层.
gatsby-transformer-json:将原始JSON字符串转换为JavaScript对象
在这里插入图片描述
npm i gatsby-source-filesystem gatsby-transformer-json

gatsby-config.js做下配置

/**
 * Configure your Gatsby site with this file.
 *
 * See: https://www.gatsbyjs.org/docs/gatsby-config/
 */

module.exports = {
  siteMetadata: {
    title: "hello Gatsby",
    description: "description in gatsby-node.js",
    author: "gatsby",
  },
  /* Your site config here */
  plugins: [
    {
      resolve: "gatsby-source-filesystem",
      options: {
        name: "json",
        path: `${__dirname}/json/`,
      },
    },
     "gatsby-transformer-json",
  ],
}

添加文件product.json

读取信息

在这里插入图片描述
查询所有数据
在这里插入图片描述

十一、图像优化

  1. 图像文件和数据文件不在源代码中的同一位置
  2. 图像路径基于构建站点的绝对路径, 而不是相对于数据的路径, 难以分析出图片的真实位置
  3. 图像没有经过任何优化操作

gatsby-source-filesystem: 用于将本地文件信息添加至数据层.
gatsby-plugin-sharp:提供本地图像的处理功能(调整图像尺寸, 压缩图像体积 等等).
gatsby-transformer-sharp: 将 gatsby-plugin-sharp 插件处理后的图像信息添加到数据层.
gatsby-image: Reac 组件, 优化图像显示, 基于 gatsby-transformer-sharp 插件转化后的数据.
1.生成多个具有不同宽度的图像版本, 为图像设置 srcset 和 sizes 属性, 因此无论您的设备是什么宽度都可以加载到合适大小的图片
2.使用"模糊处理"技术, 其中将一个20px宽的小图像显示为占位符, 直到实际图像下载完成为止.
npm install gatsby-plugin-sharp gatsby-transformer-sharp gatsby-image

gatsby-config.js

/**
 * Configure your Gatsby site with this file.
 *
 * See: https://www.gatsbyjs.org/docs/gatsby-config/
 */

module.exports = {
  siteMetadata: {
    title: "hello Gatsby",
    description: "description in gatsby-node.js",
    author: "gatsby",
  },
  /* Your site config here */
  plugins: [
    "gatsby-plugin-sharp",
    "gatsby-transformer-sharp",
  ],
}

product.js

import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"

export default function Product({ data }) {
  return data.allProductsJson.nodes.map(node => (
    <div key={node.title}>
      <p>{node.title}</p>
      <p>{node.address}</p>
      <p>{node.price}</p>
      <div style={{ width: 400 }}>
        <Img fixed={node.url.childImageSharp.fixed} />
      </div>
    </div>
  ))
}

export const query = graphql`
  query {
    allProductsJson {
      nodes {
        address
        price
        title
        url {
          childImageSharp {
            fixed(width: 200, height: 200) {
              height
              width
              src
              srcSet
            }
          }
        }
      }
    }
  }
`

数据和图片就会展示出来了
在这里插入图片描述

十三、将 markdown 数据放入数据层

构建文章列表

  1. 通过 gatsby-source-filesystem 将markdown文件数据放入数据层
    在这里插入图片描述
    构建文章列表
  2. 通过 gatsby-transformer-remark 将数据层中的原始 markdown 数据转换为对象形式
    在这里插入图片描述
    npm i gatsby-transformer-remark

gatsby-config.js里面做下配置

/**
 * Configure your Gatsby site with this file.
 *
 * See: https://www.gatsbyjs.org/docs/gatsby-config/
 */

module.exports = {
  siteMetadata: {
    title: "hello Gatsby",
    description: "description in gatsby-node.js",
    author: "gatsby",
  },
  /* Your site config here */
  plugins: [
    {
      resolve: "gatsby-source-filesystem",
      options: {
        name: "markdown",
        path: `${__dirname}/src/posts/`,
      },
    },
  “gatsby-transformer-remark”
  ],
}

```md
gatsby.md


title: “Hello Gatsby”
date: “2048-01-05”

Hello Gatsby, this is a post

``
构建文章列表
3. 组件数据查询
在这里插入图片描述

查询:
在这里插入图片描述
list.js

import React from "react"
import Header from "../components/header"
import { graphql } from "gatsby"
import SEO from "../components/SEO"

export default function List({ data }) {
  return (
    <div>
      <SEO title="list page" description="list page description" />
      list page works
      <Header />
      {data.allMarkdownRemark.nodes.map(post => (
        <div key={post.id}>
          <p>{post.frontmatter.title}</p>
          <p>{post.frontmatter.date}</p>
          <div dangerouslySetInnerHTML={{ __html: post.html }}></div>
        </div>
      ))}
    </div>
  )
}

export const query = graphql`
  query {
    allMarkdownRemark {
      nodes {
        frontmatter {
          title
          date
        }
        html
        id
      }
    }
  }
`

在这里插入图片描述

十三、以编程的方式为所有md数据节点添加slug属性

构建文章详情

  1. 重新构建查询数据, 添加 slug 作为请求标识, slug 值为文件名称
    gatsby.md -> /posts/gatsby
    react.md -> /posts/react

  2. 重新构建查询数据, 添加 slug 作为请求标识, slug 值为文件名称
    在这里插入图片描述
    构建文章详情
    gatsby-node.js

const path = require("path")

function onCreateNode({ node, actions }) {
  const { createNodeField } = actions
  if (node.internal.type === "MarkdownRemark") {
    const slug = path.basename(node.fileAbsolutePath, ".md")
    createNodeField({
      node,
      name: "slug",
      value: slug,
    })
  }
}

module.exports = {
  createPages,
  onCreateNode,
}

  1. 根据 slug 标识构建页面

在这里插入图片描述
构建文章详情
4. 组件数据查询

在这里插入图片描述
构建文章详情
5. 处理 markdown 文件中图片
gatsby-remark-images: 处理 markdown 中的图片, 以便可以在生产环境中使用
在这里插入图片描述
完整
gatsby-node.js

const path = require("path")

async function createPages({ graphql, actions }) {
  const { createPage } = actions
  // 1. 获取模板文件的绝对路径
  const template = require.resolve("./src/templates/article.js")
  // 2. 获取页面的访问标识
  let { data } = await graphql(`
    query {
      allMarkdownRemark {
        nodes {
          fields {
            slug
          }
        }
      }
    }
  `)
  // 3. 创建页面
  data.allMarkdownRemark.nodes.forEach(node => {
    createPage({
      component: template,
      path: `/article/${node.fields.slug}`,
      context: {
        slug: node.fields.slug,
      },
    })
  })
}

function onCreateNode({ node, actions }) {
  const { createNodeField } = actions
  if (node.internal.type === "MarkdownRemark") {
    const slug = path.basename(node.fileAbsolutePath, ".md")
    createNodeField({
      node,
      name: "slug",
      value: slug,
    })
  }
}

module.exports = {
  createPages,
  onCreateNode,
}

article.js

import React from "react"
import { graphql } from "gatsby"

export default function Article({ data }) {
  return (
    <div>
      <p>{data.markdownRemark.frontmatter.title}</p>
      <p>{data.markdownRemark.frontmatter.date}</p>
      <div dangerouslySetInnerHTML={{ __html: data.markdownRemark.html }}></div>
    </div>
  )
}

export const query = graphql`
  query($slug: String) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        date
      }
      id
    }
  }
`

在这里插入图片描述

十四、从strapi中获取数据

创建项目: npx create-strapi-app 项目名称
https://github.com/strapi/strapi
在这里插入图片描述

npx create-strapi-app cms

在这里插入图片描述
接口创建
在这里插入图片描述
在这里插入图片描述

分配了权限之后才可以调接口

十五、Gatsby Transformer 插件开发

数据源插件负责从 Gatsby 应用外部获取数据,创建数据查询节点供开发者使用

  1. gatsby clean 清除上一次的构建内容
    在这里插入图片描述

  2. 在项目根目录里下创建 plugins 文件夹,在此文件夹中继续创建具体的插件文件夹,比如 gatsby-source-mystrapi 文件夹

  3. 在插件文件夹中创建 gatsby-node.js 文件
    plugins/gatsby-source-mystrapi/ gatsby-node.js

const axios = require("axios")
const pluralize = require("pluralize")
const createNodeHelper = require("gatsby-node-helpers").default

async function sourceNodes({ actions }, configOptions) {
  const { createNode } = actions
  const { apiUrl, contentTypes } = configOptions
  // Post -> posts  Product -> products
  const types = contentTypes
    .map(type => type.toLowerCase())
    .map(type => pluralize(type))

  // 从外部数据源中获取数据
  let final = await getContents(types, apiUrl)
  for (let [key, value] of Object.entries(final)) {
    // 1. 构建数据节点对象 allPostsContent allProductsContent
    const { createNodeFactory } = createNodeHelper({
      typePrefix: key,
    })
    const createNodeObject = createNodeFactory("content")
    // 2. 根据数据节点对象创建节点
    value.forEach(item => {
      createNode(createNodeObject(item))
    })
  }
}

async function getContents(types, apiUrl) {
  const size = types.length
  let index = 0
  // {posts: [], prodcuts: []}
  const final = {}
  // 初始调用
  await loadContents()
  async function loadContents() {
    if (index === size) return
    console.log(`${apiUrl}/${types[index]}`)
    let { data } = await axios.get(`${apiUrl}/${types[index]}`)
    final[types[index++]] = data
    await loadContents()
  }
  return final
}

module.exports = {
  sourceNodes,
}

  1. 插件实际上就是 npm 包
  2. 导出 sourceNodes 方法用于获取外部数据,创建数据查询节点
  3. 在 gatsby-config.js 文件中配置插件,并传递插件所需的配置参数
  4. 重新运行应用

transformer 插件将 source 插件提供的数据转换为新的数据

  1. 在 plugins 文件夹中创建 gatsby-transformer-xml 文件件
  2. 在插件文件夹中创建 gatsby-node.js 文件
  3. 在文件中导出 onCreateNode 方法用于构建 Gatsby 查询节点
  4. 根据节点类型筛选 xml 节点 node.internal.mediaType -> application/xml
  5. 通过 loadNodeContent 方法读取节点中的数据
  6. 通过 xml2js 将xml数据转换为对象
  7. 将对象转换为 Gatsby 查询节点

xml/persion.xml

<person>
  <name>张三</name>
  <age>20</age>
</person>

plugins/gatsby-transformer-xml/gatsby-node.js

const { parseString } = require("xml2js")
const { promisify } = require("util")
const parse = promisify(parseString)
const createNodeHelpers = require("gatsby-node-helpers").default

async function onCreateNode({ node, loadNodeContent, actions }) {
  const { createNode } = actions
  // 判断node是否是我们需要转换的节点
  if (node.internal.mediaType === "application/xml") {
    let content = await loadNodeContent(node)
    let obj = await parse(content, {
      explicitArray: false,
      explicitRoot: false,
    })
    const { createNodeFactory } = createNodeHelpers({
      typePrefix: "XML",
    })
    const createNodeObject = createNodeFactory("parsedContent")
    createNode(createNodeObject(obj))
  }
}

module.exports = {
  onCreateNode,
}

十六、SEO 优化

gatsby-plugin-react-helmet
react-helmet 是一个组件, 用于控制页面元数据. 这对于 SEO 非常重要.
此插件用于将页面元数据添加到 Gatsby 构建的静态HTML页面中.
npm install gatsby-plugin-react-helmet react-helmet

在这里插入图片描述
创建SEO组件

src/components/SEO.js

import React from "react"
import { graphql, useStaticQuery } from "gatsby"
import { Helmet } from "react-helmet"

export default function SEO({ title, description, meta, lang }) {
  const { site } = useStaticQuery(graphql`
    query {
      site {
        siteMetadata {
          title
          description
        }
      }
    }
  `)
  const metaDescription = description || site.siteMetadata.description
  return (
    <Helmet
      htmlAttributes={{ lang }}
      title={title}
      titleTemplate={`%s | ${site.siteMetadata.title}`}
      meta={[
        {
          name: "description",
          content: metaDescription,
        },
      ].concat(meta)}
    />
  )
}

SEO.defaultProps = {
  description: "",
  meta: [],
  lang: "en",
}

使用组件
pages/list.js

import React from "react"
import Header from "../components/header"
import { graphql } from "gatsby"
import SEO from "../components/SEO"

export default function List({ data }) {
  return (
    <div>
      <SEO title="list page" description="list page description" />
      list page works
      <Header />
      {data.allMarkdownRemark.nodes.map(post => (
        <div key={post.id}>
          <p>{post.frontmatter.title}</p>
          <p>{post.frontmatter.date}</p>
          <div dangerouslySetInnerHTML={{ __html: post.html }}></div>
        </div>
      ))}
    </div>
  )
}

export const query = graphql`
  query {
    allMarkdownRemark {
      nodes {
        frontmatter {
          title
          date
        }
        html
        id
      }
    }
  }
`

十七、Less 支持

在 gatsby 应用中使用 less
下载插件:npm install --save gatsby-plugin-less
配置插件:plugins: [gatsby-plugin-less]
创建样式:index.module.less
引入样式:import styles from './index.module.less

十八、创建layout.js

公共部分放进去

import React from "react"
import Header from "./Header"
import Footer from "./Footer"

export default function Layout({ children }) {
  return (
    <>
      <Header />
      {children}
      <Footer />
    </>
  )
}

创建gatsby-browser.js做下配置

const React = require("react")
const Layout = require("./src/components/Layout").default
const { Provider } = require("react-redux")
const createStore = require("./src/store/createStore").default
const axios = require("axios")

axios.defaults.baseURL = "https://conduit.productionready.io/api"

exports.wrapPageElement = ({ element }) => {
  return <Layout>{element}</Layout>
}
const React = require("react")
const Layout = require("./src/components/Layout").default
const { Provider } = require("react-redux")
const createStore = require("./src/store/createStore").default
const axios = require("axios")

axios.defaults.baseURL = "https://conduit.productionready.io/api"
// layout
exports.wrapPageElement = ({ element }) => {
  return <Layout>{element}</Layout>
}
// redux
exports.wrapRootElement = ({ element }) => {
  return <Provider store={createStore()}>{element}</Provider>
}

十九、受保护客户端路由

pages/app.js

import React from "react"
import { Router } from "@reach/router"
import Settings from "../components/settings"
import PrivateRoute from "../components/PrivateRoute"
import Create from "../components/create"

export default function App() {
  return (
    <Router>
      <PrivateRoute component={Settings} path="/app/settings" />
      <PrivateRoute component={Create} path="/app/create" />
    </Router>
  )
}

PrivateRoute.js

import React from "react"
import useLogin from "../hooks/useLogin"
import { navigate } from "gatsby"

export default function PrivateRoute({ component: Component, ...rest }) {
  const [isLogin, loading] = useLogin()
  if (loading) return null
  if (isLogin) return <Component {...rest}></Component>
  navigate("/login")
  return null
}

gatsby-config.js做下配置

/**
 * Configure your Gatsby site with this file.
 *
 * See: https://www.gatsbyjs.org/docs/gatsby-config/
 */

module.exports = {
  /* Your site config here */
  plugins: [
    {
      resolve: "gatsby-plugin-create-client-paths",
      options: { prefixes: ["/app/*"] },
    },
    {
      resolve: "gatsby-source-list",
      options: { apiUrl: "https://conduit.productionready.io/api" },
    },
    "gatsby-plugin-article",
    "gatsby-disable-404",
  ],
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值