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
}
}
}
图像优化
存在的问题
- 图像文件和数据文件不在源代码中的同一个位置
- 图像文件存储在
static
- 数据文件存储在
json
- 图像文件存储在
- 图像路径基于构建站点的绝对路径,而不是相对于数据(
.json
文件)的路径,难以分析出图片的真实存储位置 - 图像没有经过任何优化操作
- 压缩、生成响应式图片等
安装插件
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
插件转化后的数据。
优化内容
- 生成多个具有不同宽度的响应式图像版本,为图像设置
srcset
和sizes
属性,不同宽度的设备加载合适大小的图片 - 使用“模糊处理”技术,加载图片时,使用一个 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",
],
}
改动图像资源
将图像文件夹 images
从 static
移动到数据文件所在的 json
文件夹下。
修改数据中的图像路径:"/images/xxxx" -> "./images/xxxx"
。
查看 GraphQL
重启项目 gatsby develop
,打开 GraphQL 调试页面。
查询数据多了 allImageSharp
和 imageSharp
节点,而 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 调试页面的查询数据中多了 allMarkdownRemark
和 markDownRemark
节点:
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
添加方式有两种:
- 手动在每个 Markdown 文件中添加
slug
属性 - 【推荐】使用编程方式一次性为数据层中的每篇 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 标签中
},
},
],
},
},
],
}