Gatsby 学习 - 03 Gatsby 插件

Gatsby 插件

Gatsby 框架内置插件系统,插件是为应用添加功能的最好的方式。

在 Gatsby 中有三种类型的插件:

  • 数据源插件(source):负责从应用外部获取数据,将数据统一放在 Gatsby 的数据层中
  • 数据转换插件(transformer):负责转换特定类型的数据的格式,比如将 Markdown 文件中的内容转化成对象形式
  • 功能插件(plugin):为应用提供功能,比如通过插件让应用支持 Less 或 TypeScript

Gatsby 官方提供了非常多且非常有用的插件,开发者也可以自己编写插件,以及发布供他人使用。

Gatsby 对插件的命名规范有要求:

  • 数据源插件名称中必须包含 source
  • 数据转换插件名称中必须包含 transformer
  • 功能插件名称中必须包含 plugin

将本地 JSON 文件数据添加到数据层

添加示例文件

在根目录下创建 json 文件夹,在里面添加 products.json 文件。

json\products.json

[
  {
    "title": "Vans范斯 男子短袖T恤 Work Weird新款运动休闲TEE迷彩官方正品",
    "price": 208,
    "url": "/images/product-1.jpg",
    "address": "上海",
    "id": "1"
  },
  {
    "title": "小ck女包包2020新款潮真皮斜挎包女百搭ins2019单肩包时尚手提包",
    "price": 239,
    "url": "/images/product-2.webp",
    "address": "北京",
    "id": "2"
  },
  {
    "title": "潮牌羊毛开衫男川久爱心毛衣保桃心play外套玲",
    "price": 118,
    "url": "/images/product-3.webp",
    "address": "深圳",
    "id": "3"
  },
  {
    "title": "太平鸟男装 2020秋季pupu熊联名时尚大图案毛套衫圆领帅气针织衫",
    "price": 379,
    "url": "/images/product-4.webp",
    "address": "广州",
    "id": "4"
  }
]

JSON 中使用的图片放置在根目录的 static 文件夹中。

static 文件夹是静态资源文件夹,里面的资源在浏览器中可以直接访问,例如 http://localhost:8000/images/product-1.jpg 可以访问 static/images/product-1.jpg 文件。

安装插件

将本地 JSON 文件中的数据放入数据层需要安装两个插件:

  • gatsby-source-filesystem:用于将本地文件中的数据添加至数据层
  • gatsby-transformer-json:将原始 JSON 字符串转换成 JavaScript 对象

安装完成后,需要将插件配置到 gatsby-config.js 中:

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

module.exports = {
  // siteMetadata 对象用于配置网站元数据
  siteMetadata: {
    title: "Hello Gatsby",
    author: "Jone",
  },
  /* Your site config here */
  plugins: [
    {
      resolve: "gatsby-source-filesystem", // 插件名
      options: {
        // 分类名称,用于区分不同数据,可以不设置
        name: "json",
        // 插件将转化 path 文件夹下的 json 文件
        // 最终以<文件名Json>为数据名存储到数据层
        path: `${__dirname}/json/`,
      },
    },
    // 不需要特殊配置的可以直接放到 plugin 数组中
    "gatsby-transformer-json",
  ],
}

GraphQL 查询

query MyQuery {
  allFile {
    nodes { # 转化的文件
      absolutePath # 文件的绝对路径
      publicURL # 转化后的文件所在路径
      ext # 文件的扩展名
    }
  }
  productsJson { # 转化的数据(查询单个数据:只查询第一条)
    title
    url
    price
    address
  }
  allProductsJson { # 转化的数据(查询全部数据)
    nodes {
      title
      url # 取的 JSON 数据的 url 字段的值
      price
      address
    }
  }
}

图像优化

存在的问题

  1. 图像文件和数据文件不在源代码中的同一个位置
    • 图像文件存储在 static
    • 数据文件存储在 json
  2. 图像路径基于构建站点的绝对路径,而不是相对于数据(.json 文件)的路径,难以分析出图片的真实存储位置
  3. 图像没有经过任何优化操作
    • 压缩、生成响应式图片等

安装插件

  • gatsby-source-filesystem:用于将本地文件信息添加到数据层
  • gatsby-plugin-sharp:提供本地图像的处理功能(调整图像尺寸、压缩体积等)
    • 依赖的 sharp 模块可能受国外访问限制,可以配置镜像
    • 依赖的 mozjpeg 模块从 github 下载安装包,如果 github 访问受限,可以配置 hosts
    • PS:配置 VPN 一劳永逸
  • gatsby-transformer-sharp:将 gatsby-plugin-sharp插件处理后的图像信息添加到数据层
    • 该插件会识别数据中的图片地址,如果定位的图片文件被 gatsby-source-filesystem 添加到数据层,则会将 url 的 GraphQL 查询结果替换为图片信息对象
  • gatsby-image:React 组件,优化图像显示,基于gatsby-transformer-sharp插件转化后的数据。

优化内容

  1. 生成多个具有不同宽度的响应式图像版本,为图像设置 srcsetsizes 属性,不同宽度的设备加载合适大小的图片
  2. 使用“模糊处理”技术,加载图片时,使用一个 20px 宽的小图像显示为占位符,直到实际图像下载完成为止

配置插件

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

module.exports = {
  // siteMetadata 对象用于配置网站元数据
  siteMetadata: {
    title: "Hello Gatsby",
    author: "Jone",
  },
  /* Your site config here */
  plugins: [
    {
      resolve: "gatsby-source-filesystem", // 插件名
      options: {
        // 分类名称,用于区分不同数据,可以不设置
        name: "json",
        // 插件将转化 path 文件夹下的 json 文件
        // 最终以<文件名Json>为数据名存储到数据层
        path: `${__dirname}/json/`,
      },
    },
    // 不需要特殊配置的可以直接放到 plugin 数组中
    "gatsby-transformer-json",
    "gatsby-plugin-sharp",
    "gatsby-transformer-sharp",
  ],
}

改动图像资源

将图像文件夹 imagesstatic 移动到数据文件所在的 json 文件夹下。

修改数据中的图像路径:"/images/xxxx" -> "./images/xxxx"

查看 GraphQL

重启项目 gatsby develop,打开 GraphQL 调试页面。

查询数据多了 allImageSharpimageSharp 节点,而 JSON 数据项的 url 节点也增加了相应节点:

query MyQuery {
  allImageSharp {
    nodes {
      fixed { # 固定大小的图片(可以生成指定宽高的图片)
        src
      }
      fluid { # 响应大小的图片
        src
      }
    }
  }
  allProductsJson {
    nodes {
      title
      price
      address
      url {
        childImageSharp {
          fluid {
            # 响应图片相关属性
            src
            srcSet
            sizes
            aspectRatio # 像素比
          }
          fixed(width:200,height:200) { # 指定宽高
            src
            srcSet
            width
            height
          }
        }
      }
    }
  }
}

显示图片

响应式图片:

// src\pages\product.js
import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"

export default function Product({ data }) {
  return (
    <div>
      {data.allProductsJson.nodes.map((node, index) => (
        <div key={index}>
          <p>{node.title}</p>
          <p>{node.address}</p>
          <p>{node.price}</p>
          <div style={{ width: 400 }}>
            {/* fluid 接收响应式图片 图片的宽以父元素为准 */}
            <Img fluid={node.url.childImageSharp.fluid} />
          </div>
        </div>
      ))}
    </div>
  )
}

// 响应式图片
export const query = graphql`
  query {
    allProductsJson {
      nodes {
        title
        url {
          childImageSharp {
            fluid {
              src
              srcSet
              sizes
              aspectRatio
            }
          }
        }
        price
        address
      }
    }
  }
`

固定大小图片:

// src\pages\product.js
import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"

export default function Product({ data }) {
  return (
    <div>
      {data.allProductsJson.nodes.map((node, index) => (
        <div key={index}>
          <p>{node.title}</p>
          <p>{node.address}</p>
          <p>{node.price}</p>
          <div style={{ width: 400 }}>
            {/* fixed 接收固定大小图片 图片的宽高固定 */}
            <Img fixed={node.url.childImageSharp.fixed} />
          </div>
        </div>
      ))}
    </div>
  )
}

// 固定大小图片
export const query = graphql`
  query {
    allProductsJson {
      nodes {
        title
        url {
          childImageSharp {
            fixed(width: 200, height: 200) {
              src
              srcSet
              height
              width
            }
          }
        }
        price
        address
      }
    }
  }
`

将本地 Markdown 数据添加到数据层

需求描述

  • 在本地有多个 Markdown 文件,一个文件代表一篇文章
  • 将本地 Markdown 文件的数据添加到数据层,构建文章列表页和详情页。

示例文件

添加文件 /posts/gatsby.md

---
title: "Hello Gatsby"
date: "2048-01-05"
---

Hello Gatsby, this is a post.


添加文件 /posts/react.md

---
title: "Hello React"
date: "9012-12-23"
---

This is a test markdown file

![](./images/1.jpg)

添加图片 /posts/images/1.jpg

安装插件

  • gatsby-source-filesystem:将 Markdown 文件数据放入数据层
    • 仅使用此插件,放入的只是原始 Markdown 数据,无法使用
  • gatsby-transformer-remark:将数据层中的原始 Markdown 数据转化成可用的对象形式

配置插件

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

module.exports = {
  // siteMetadata 对象用于配置网站元数据
  siteMetadata: {
    title: "Hello Gatsby",
    author: "Jone",
  },
  /* Your site config here */
  plugins: [
    {
      resolve: "gatsby-source-filesystem", // 插件名
      options: {
        // 分类名称,用于区分不同数据,可以不设置
        name: "json",
        // 插件将转化 path 文件夹下的 json 文件
        // 最终以<文件名Json>为数据名存储到数据层
        path: `${__dirname}/json/`,
      },
    },
    {
      resolve: "gatsby-source-filesystem",
      options: {
        name: "markdown",
        path: `${__dirname}/posts/`,
      },
    },
    // 不需要特殊配置的可以直接放到 plugin 数组中
    "gatsby-transformer-json",
    "gatsby-plugin-sharp",
    "gatsby-transformer-sharp",
    "gatsby-transformer-remark",
  ],
}

重新启动 gatsby develop

查看 GraphQL

GraphQL 调试页面的查询数据中多了 allMarkdownRemarkmarkDownRemark 节点:

query MyQuery {
  allMarkdownRemark {
    nodes {
      html # 解析的内容部分
      frontmatter {
        date # 解析的 date 字段
        title # 解析的 title 字段
      }
      fileAbsolutePath # markdown 文件的绝对路径
      internal { # 内部属性
        type # 标识文件内部得一个类型,用于区分文件类型,如 markdown 文件、json 文件、页面文件等
      }
    }
  }
}

构建列表页面

// src\pages\list.js
import React from "react"
import Header from "../components/Header"
import { graphql } from "gatsby"

export default function List({ data }) {
  return (
    <div>
      List Page
      <Header />
      <hr />
      {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 {
        id
        html
        frontmatter {
          date
          title
        }
      }
    }
  }
`

构建文章详情页

重新构建查询数据

为每篇文章的数据添加 slug (访问路径)作为请求标识,slug 值为文件名称。

  • gatsby.md -> /posts/gatsby
  • react.md -> /posts/react

添加方式有两种:

  1. 手动在每个 Markdown 文件中添加 slug 属性
  2. 【推荐】使用编程方式一次性为数据层中的每篇 Markdown 文章添加 slug 属性

编程方式添加 slugn 属性

gatsby-node.js 文件中导出一个 onCreateNode 方法,该方法在创建每个节点(Node)后被调用。

Gatsby 应用启动时,会先启动插件,通过插件从外部数据源获取数据,然后将获取到的数据添加到数据层中,向数据层中每添加一条数据实际上就是创建一个数据节点,每创建完一个数据节点,Gatsby 都会调用一次 onCreateNode 方法。

onCreateNode 是框架为开发者提供的用于修改节点信息的方法。

onCreateNode 接收的参数中可以解构出 node 对象,就是 Gatsby 创建的数据节点:

  • 通过数据节点的内部 type 属性,可以获取当前节点的内部类型,区分 Markdown 文件。
  • 通过数据节点的 fileAbsolutePath 文件绝对路径属性,获取文件名作为 slug 的值。

最后使用从 actions 对象中解构的 createNodeField 方法为数据添加属性,方法接受三个参数:

  • node:要添加属性的数据节点对象
  • name:要添加的属性的名字
  • value:要添加的属性的值
// 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 }

重启 gatsby develop

查看 GraphQL

createNodeField 添加的属性,会添加到 nodes 节点下的 fields 属性下。

query MyQuery {
  allMarkdownRemark {
    nodes {
      html
      frontmatter {
        date
        title
      }
      fields {
        slug
      }
    }
  }
}

根据 slug 构建文章详情页

创建页面

使用 createPages 编程式创建页面:

  • 从参数中解构 graphql 方法,用于查询数据
    • gatsby 模块中的 graphql 方法不同
    • 它接收一个查询命令的字符串
    • 返回 Promise,值是查询结果
  • 通过 context 传递给页面的数据,可以在页面中使用的查询命令中作为参数接收 $<参数名>
// gatsby-node.js
const path = require("path")

// 创建文章详情页
async function createPages({ graphql, actions }) {
  // 获取模板绝对路径
  const template = require.resolve("./src/templates/post.js")
  // 获取组件所需数据
  // 注意此处的 graphq 和 组件中使用的 graphql 传参方式不一样
  const { data } = await graphql(`
    query {
      allMarkdownRemark {
        nodes {
          fields {
            slug
          }
        }
      }
    }
  `)

  // 根据模板和数据创建页面
  const { createPage } = actions
  data.allMarkdownRemark.nodes.forEach(node => {
    createPage({
      path: `/posts/${node.fields.slug}`, // 页面访问地址
      component: template, // 模板绝对路径
      // 组件中使用的查询命令可以通过 `$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 }

查询命令

在这里插入图片描述

文章详情页模板

// src\templates\person.js
import React from "react"
import { graphql } from "gatsby"

export default function Post({ 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 {
        date
        title
      }
      id
    }
  }
`

处理 Markdown 文件中的图片

react.md 中插入了一个图片 ./images/1.jpg,在转化数据时,并没有将 Markdown 数据中的图片地址替换为这个图片资源文件转化后的路径。

gatsby-remark-images 插件用于处理 Markdown 中的图片,将图片路径替换为转化后的资源路径,以及一些其它优化。

安装 npm i gatsby-remark-images

该插件要作为 gatsby-transformer-remark 的插件去配置:

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

module.exports = {
  // siteMetadata 对象用于配置网站元数据
  siteMetadata: {
    title: "Hello Gatsby",
    author: "Jone",
  },
  /* Your site config here */
  plugins: [
    {
      resolve: "gatsby-source-filesystem", // 插件名
      options: {
        // 分类名称,用于区分不同数据,可以不设置
        name: "json",
        // 插件将转化 path 文件夹下的 json 文件
        // 最终以<文件名Json>为数据名存储到数据层
        path: `${__dirname}/json/`,
      },
    },
    {
      resolve: "gatsby-source-filesystem",
      options: {
        name: "markdown",
        path: `${__dirname}/posts/`,
      },
    },
    // 不需要特殊配置的可以直接放到 plugin 数组中
    "gatsby-transformer-json",
    "gatsby-plugin-sharp",
    "gatsby-transformer-sharp",
    {
      resolve: "gatsby-transformer-remark",
      options: {
        plugins: [
          {
            resolve: "gatsby-remark-images",
            options: {
              maxWidth: 600, // 生成图片的最大宽度,默认 650
              linkImagesToOriginal: false, // 是否将图片包裹在 a 标签中
            },
          },
        ],
      },
    },
  ],
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值