文章详情页
文章详情页要使用编程的方式去创建,并且要通过本地插件去实现。
创建本地插件
创建 plugins/gatsby-plugin-article/gatsby-node.js,在 plugins/gatsby-plugin-article 插件文件夹下生成 package.json(npm init -y)。
配置插件
// gatsby-config.js
module.exports = {
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-source-list 后面
// 因为只有文章数据添加到数据层
// 才能基于数据层的数据创建详情页
"gatsby-plugin-article",
],
}
编写插件
// plugins\gatsby-plugin-article\gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
const { data } = await graphql(`
query MyQuery {
allArticlesList {
nodes {
# 基于 slug 构建详情页
# 用作地址
# 用作详情页查询数据的依据
slug
}
}
}
`)
const { createPage } = actions
data.allArticlesList.nodes.forEach(article => {
createPage({
path: `/article/${article.slug}`, // 访问地址
component: require.resolve("../../src/templates/article.js"), // 模板绝对路径
context: {
slug: article.slug, // 传递给页面的参数
},
})
})
}
编写页面模板
页面模板使用 src/pages/article.js 文件,将其移动到 src/templates 下。
// src\templates\article.js
import React from "react"
import { graphql } from "gatsby"
export default function Article({ data }) {
const article = data.articlesList
return (
<div className="article-page">
<div className="banner">
<div className="container">
<h1>{article.title}</h1>
<div className="article-meta">
<a>
<img src={article.author.image} />
</a>
<div className="info">
<a className="author">{article.author.username}</a>
<span className="date">{article.createAt}</span>
</div>
<button className="btn btn-sm btn-outline-secondary">
<i className="ion-plus-round" />
{article.author.username}{" "}
<span className="counter">({article.following})</span>
</button>
<button className="btn btn-sm btn-outline-primary">
<i className="ion-heart" />
Favorite Post{" "}
<span className="counter">({article.favoritesCount})</span>
</button>
</div>
</div>
</div>
<div className="container page">
<div className="row article-content">
<div dangerouslySetInnerHTML={{ __html: article.body }}></div>
</div>
<hr />
{/* 点赞&关注区域 */}
<div className="article-actions">...</div>
{/* 评论区域 */}
<div className="row">...</div>
</div>
</div>
)
}
export const query = graphql`
query ($slug: String) {
articlesList(slug: { eq: $slug }) {
author {
image # 作者头像
username # 作者名称
following # 作者关注数量
}
title # 标题
body # 文章内容
description # 描述
createdAt # 创建日期
favoritesCount # 点赞数
}
}
`
在列表页添加详情链接
// src\templates\list.js
// 引入 Link 组件
import { graphql, Link } from "gatsby"
// 修改 Lists 组件
<Link to={`/article/${article.slug}`} className="preview-link">
<h1>{article.title}</h1>
<p>{article.description}</p>
<span>Read more...</span>
</Link>
当前列表页加载后获取了最新文章数据,新的文章点击查看详情页面,由于没有构建对应的静态页面,可能会报 404 页面。
可以先把获取最新数据的代码注释掉再测试。
创建动态文章详情页面
存在的问题
列表页面最先显示的是 Gatsby 构建的静态页面,接着动态获取文章最新数据,替换静态数据。
但是获取的最新文章并没有在 Gatsby 构建应用时生成对应的静态页面。
所以当点击列表上的最新文章页面的时候(如果静态数据中没有该文章),则会进入 404 页面。
所以需要实现:无论用户访问的是静态生成的详情页还是动态生成的详情页,都能正常访问。
解决方法
自定义应用的 404 页面:
- 在 404 页面可以获取到页面的访问路径。
- 检查路径是否以
/article开头,如果是就获取路径上的slug - 然后将
slug传递给article.js组件 - 在
article.js组件中先判断是否有静态数据,如果有则显示静态数据 - 如果没有则通过
slug动态获取数据,然后将获取的数据显示在页面中
创建 404 页面
新建 src/pages/404.js 文件,用于自定义 404 页面。
// src\pages\404.js
import React from "react"
export default function NotFount({ location }) {
const { pathname } = location
return <div>{pathname}</div>
}
现在 404 页面仍然显示的是应用默认的内容。
点击 404 页面上面的 Preview custom 404 page按钮可以查看自定义的内容。

将文章数据作为状态存储
注意文章数据初始值是 {},需要对更深的取值使用可选链(?.)
// src\templates\article.js
import React, { useState, useEffect } from "react"
import { graphql } from "gatsby"
import axios from "axios"
export default function Article({ data, slug }) {
const [article, setArticle] = useState({})
useEffect(() => {
if (data) {
setArticle(data.articlesList)
return
}
;(async function () {
const { data } = await axios.get(`/articles/${slug}`)
setArticle(data.article)
})()
}, [])
return (
<div className="article-page">
<div className="banner">
<div className="container">
<h1>{article.title}</h1>
<div className="article-meta">
<a>
<img src={article.author?.image} />
</a>
<div className="info">
<a className="author">{article.author?.username}</a>
<span className="date">{article.createAt}</span>
</div>
<button className="btn btn-sm btn-outline-secondary">
<i className="ion-plus-round" />
{article.author?.username}{" "}
<span className="counter">({article.following})</span>
</button>
<button className="btn btn-sm btn-outline-primary">
<i className="ion-heart" />
Favorite Post{" "}
<span className="counter">({article.favoritesCount})</span>
</button>
</div>
</div>
</div>
<div className="container page">
<div className="row article-content">
<div dangerouslySetInnerHTML={{ __html: article.body }}></div>
</div>
<hr />
{/* 点赞&关注区域 */}
<div className="article-actions">...</div>
{/* 评论区域 */}
<div className="row">...</div>
</div>
</div>
)
}
export const query = graphql`
query ($slug: String) {
articlesList(slug: { eq: $slug }) {
author {
image # 作者头像
username # 作者名称
following # 作者关注数量
}
title # 标题
body # 文章内容
description # 描述
createdAt # 创建日期
favoritesCount # 点赞数
}
}
`
禁用 Gatsby 404 页面
开发环境下(gatsby develop),当 Gatsby 进入 404 时,会先进入 dev-404-page,点击 Preview custom 404 page 才会进入默认创建的 404 页面或开发者自定义的 404 页面。
所以导致现在访问动态数据的文章详情页,页面会显示两个 Layout 内容。
可以使用 Gatsby Node APIs 在创建完页面后删除开发环境的 dev-404-page 页面,就可以直接访问自定义的 404 页面了。
可以直接在应用的 gatsby-node.js 中配置,也可以创建自定义插件。
创建自定义插件plugins/gatsby-plugin-disable-404/gatsby-node.js。
在插件目录下生成 package.json:npm init -y。
在应用中配置插件
// gatsby-config.js
module.exports = {
plugins: [
...
"gatsby-plugin-disable-404",
],
}
编写插件:
// plugins\gatsby-plugin-disable-404\gatsby-node.js
exports.onCreatePage = ({ page, actions }) => {
const { deletePage, createPage } = actions
if (page.path === "/dev-404-page/") {
// 删除开发环境的 dev-404-page 页面
deletePage(page)
} else {
createPage(page)
}
}
该插件参考
gatsby-disable-404插件,源码中删除了/404/页面,也就是将自定义的 404 页面也删除了,不能满足当前需求,所以本例重新实现。"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.onCreatePage = function (_a) { var page = _a.page, actions = _a.actions; var deletePage = actions.deletePage, createPage = actions.createPage; if (page.path === '/404/' || page.path === '/dev-404-page/') { deletePage(page); } else { createPage(page); } };
本文介绍如何使用Gatsby创建博客文章详情页,包括本地插件的创建、页面模板编写及动态文章详情页的实现方法。

被折叠的 条评论
为什么被折叠?



