基于许多 Contentful 引用使用 Gatsby 动态创建页面
Dynamically create pages with Gatsby based on many Contentful references
我目前正在使用 Gatsby collection routes API 使用来自 Contentful 的数据为一个简单的博客创建页面。
例如,为每个博文类别创建一个页面:
-- src/pages/categories/{contentfulBlogPost.category}.js
export const query = graphql`
query categoriesQuery($category: String = "") {
allContentfulBlogPost(filter: { category: { eq: $category } }) {
edges {
node {
title
category
description {
description
}
...
}
}
}
}
...
[React component mapping all blogposts from each category in a list]
...
这工作正常。
但现在我希望每篇博文有多个类别,所以我切换到 Contentful 的 references, many
内容类型,它允许一个字段有多个条目:
现在,我在字段 category2
上的 graphQL 查询结果是每个博文的不同类别数组:
查询:
query categoriesQuery {
allContentfulBlogPost {
edges {
node {
category2 {
id
name
slug
}
}
}
}
}
输出:
{
"data": {
"allContentfulBlogPost": {
"edges": [
{
"node": {
"category2": [
{
"id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
"name": "Test",
"slug": "test"
},
{
"id": "568r9e48-t1i8-sx4t8-9742-cdf70c4ed789vtu",
"name": "Test2",
"slug": "test-2"
}
]
}
},
{
"node": {
"category2": [
{
"id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
"name": "Test",
"slug": "test"
}
]
}
},
...
既然类别在数组中,我不知道如何:
- 写一个查询变量来过滤类别名称;
- 使用 slug 字段作为动态创建页面的路径。
对于我正在做的博文作者:
query authorsQuery($author__slug: String = "") {
allContentfulBlogPost(filter: { author: { slug: { eq: $author__slug } } }) {
edges {
node {
id
author {
slug
name
}
...
}
...
}
并使用 src/pages/authors/{contentfulBlogPost.author__slug}.js
创建页面
我想我必须改用 createPages API。
您可以使用文件系统 API 获得结果,像这样的方法可能有效:
src/pages/category/{contentfulBlogPost.category2__name}.js
在这种情况下,似乎这种方法可能会导致一些注意事项,因为您可能会创建具有相同 URL (slug
) 的重复页面,因为帖子可能包含多个重复的类别。
但是,我认为如您所说,使用 createPages
API 更简洁,请记住您需要处理类别以避免重复,因为它们位于 一对多关系。
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allContentfulBlogPost {
edges {
node {
category2 {
id
name
slug
}
}
}
}
}
`)
let categories= { slugs: [], names: [] };
result.data.allContentfulBlogPost.edges.map(({node}))=> {
let { name, slug } = node.category2;
// make some checks if needed here
categories.slugs.push(slug);
categories.names.push(name);
return new Set(categories.slugs) && new Set(categories.names);
});
categories.slugs.forEach((category, index) => {
let name = categories.names[index];
createPage({
path: `category/${category}`,
component: path.resolve(`./src/templates/your-category-template.js`),
context: {
name
}
});
});
}
该代码不言自明。基本上,您正在定义一个包含两个数组的空对象 (categories
),slugs
和 names
:
let categories= { slugs: [], names: [] };
之后,您只需要循环查询结果(result
)并将字段值(name
、slug
和其他需要的值推送到前一个数组,如果需要进行必要的检查(以避免推送空值,或匹配某些正则表达式等)和 return a new Set
以删除重复项。
然后,您只需要循环遍历 slug 以使用 createPage
API 创建页面并通过上下文传递所需的数据:
context: {
name
}
由于冗余,这与执行相同:
context: {
name: name
}
因此,在您的模板中,您将获得 pageContext
props
中的名称。如果需要,将其替换为 slug
,具体取决于您的情况和用例,方法完全相同。
我目前正在使用 Gatsby collection routes API 使用来自 Contentful 的数据为一个简单的博客创建页面。
例如,为每个博文类别创建一个页面:
-- src/pages/categories/{contentfulBlogPost.category}.js
export const query = graphql`
query categoriesQuery($category: String = "") {
allContentfulBlogPost(filter: { category: { eq: $category } }) {
edges {
node {
title
category
description {
description
}
...
}
}
}
}
...
[React component mapping all blogposts from each category in a list]
...
这工作正常。
但现在我希望每篇博文有多个类别,所以我切换到 Contentful 的 references, many
内容类型,它允许一个字段有多个条目:
现在,我在字段 category2
上的 graphQL 查询结果是每个博文的不同类别数组:
查询:
query categoriesQuery {
allContentfulBlogPost {
edges {
node {
category2 {
id
name
slug
}
}
}
}
}
输出:
{
"data": {
"allContentfulBlogPost": {
"edges": [
{
"node": {
"category2": [
{
"id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
"name": "Test",
"slug": "test"
},
{
"id": "568r9e48-t1i8-sx4t8-9742-cdf70c4ed789vtu",
"name": "Test2",
"slug": "test-2"
}
]
}
},
{
"node": {
"category2": [
{
"id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
"name": "Test",
"slug": "test"
}
]
}
},
...
既然类别在数组中,我不知道如何:
- 写一个查询变量来过滤类别名称;
- 使用 slug 字段作为动态创建页面的路径。
对于我正在做的博文作者:
query authorsQuery($author__slug: String = "") {
allContentfulBlogPost(filter: { author: { slug: { eq: $author__slug } } }) {
edges {
node {
id
author {
slug
name
}
...
}
...
}
并使用 src/pages/authors/{contentfulBlogPost.author__slug}.js
我想我必须改用 createPages API。
您可以使用文件系统 API 获得结果,像这样的方法可能有效:
src/pages/category/{contentfulBlogPost.category2__name}.js
在这种情况下,似乎这种方法可能会导致一些注意事项,因为您可能会创建具有相同 URL (slug
) 的重复页面,因为帖子可能包含多个重复的类别。
但是,我认为如您所说,使用 createPages
API 更简洁,请记住您需要处理类别以避免重复,因为它们位于 一对多关系。
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allContentfulBlogPost {
edges {
node {
category2 {
id
name
slug
}
}
}
}
}
`)
let categories= { slugs: [], names: [] };
result.data.allContentfulBlogPost.edges.map(({node}))=> {
let { name, slug } = node.category2;
// make some checks if needed here
categories.slugs.push(slug);
categories.names.push(name);
return new Set(categories.slugs) && new Set(categories.names);
});
categories.slugs.forEach((category, index) => {
let name = categories.names[index];
createPage({
path: `category/${category}`,
component: path.resolve(`./src/templates/your-category-template.js`),
context: {
name
}
});
});
}
该代码不言自明。基本上,您正在定义一个包含两个数组的空对象 (categories
),slugs
和 names
:
let categories= { slugs: [], names: [] };
之后,您只需要循环查询结果(result
)并将字段值(name
、slug
和其他需要的值推送到前一个数组,如果需要进行必要的检查(以避免推送空值,或匹配某些正则表达式等)和 return a new Set
以删除重复项。
然后,您只需要循环遍历 slug 以使用 createPage
API 创建页面并通过上下文传递所需的数据:
context: {
name
}
由于冗余,这与执行相同:
context: {
name: name
}
因此,在您的模板中,您将获得 pageContext
props
中的名称。如果需要,将其替换为 slug
,具体取决于您的情况和用例,方法完全相同。