我正试图在博客文章上添加标签,但我很难找到一个资源来解释如何实现它们。
最终目标是获得可点击的标签,这会导致一个页面,所有带有相同标签的帖子都会出现在列表中。
我正在使用具有Contentful集成的GatsbyJS。
我有一个名为article-post.tsx的文件,代码如下:
import React from "react"
import { graphql } from "gatsby"
import { documentToReactComponents } from "@contentful/rich-text-react-renderer"
import Layout from "../components/layout/layout"
import Img from "gatsby-image"
import SEO from "../components/layout/seo"
import styled from "styled-components"
import { BodyMain, H1 } from "../components/styles/TextStyles"
export const query = graphql`
query($slug: String!) {
contentfulArticlePost(slug: { eq: $slug }) {
title
tags
publishedDate(formatString: "Do MMMM, YYYY")
featuredImage {
fluid(maxWidth: 720) {
...GatsbyContentfulFluid
}
}
body {
json
}
}
}
`
const ArticlePost = props => {
const options = {
renderNode: {
"embedded-asset-block": node => {
const alt = node.data.target.fields.title["en-US"]
const url = node.data.target.fields.file["en-US"].url
return <img alt={alt} src={url} className="embeddedImage" />
},
},
}
return (
<Layout>
<SEO title={props.data.contentfulArticlePost.title} />
<Wrapper>
<ImageWrapper>
{props.data.contentfulArticlePost.featuredImage && (
<Img
className="featured"
fluid={props.data.contentfulArticlePost.featuredImage.fluid}
alt={props.data.contentfulArticlePost.title}
/>
)}
</ImageWrapper>
<Title>{props.data.contentfulArticlePost.title}</Title>
<Tags>
{props.data.contentfulArticlePost.tags.map(tag => (
<span className="tag" key={tag}>
{tag}
</span>
))}
</Tags>
<ContentWrapper>
{documentToReactComponents(
props.data.contentfulArticlePost.body.json,
options
)}
</ContentWrapper>
</Wrapper>
</Layout>
)
}
export default ArticlePost
const Wrapper = styled.div`
display: grid;
grid-gap: 1.875rem;
margin: 0 auto;
padding: 7rem 1.875rem;
max-width: 900px;
`
const ImageWrapper = styled.div`
.featured {
border-radius: 15px;
}
`
const Title = styled(H1)`
margin: 0 auto;
text-align: center;
`
const Tags = styled.div`
margin: 0 auto;
.tag {
background: #8636E4;
border-radius: 1rem;
padding: 0.5rem;
margin: 0.2rem;
font-weight: 600;
}
`
const ContentWrapper = styled(BodyMain)`
display: grid;
grid-gap: 20px;
max-width: 900px;
margin: 0 auto;
line-height: 1.6;
.embeddedImage {
padding: 50px 0px;
width: 100%;
height: auto;
}
`
它确实给了我标签,我可以设计它们的样式。尽管我不知道如何让它们像链接/按钮一样可点击。
我有一个名为gatsby-node.js的文件,其中包含以下代码:
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const response = await graphql(`
query {
allContentfulArticlePost {
edges {
node {
id
slug
}
}
}
}
`)
response.data.allContentfulArticlePost.edges.forEach(edge => {
createPage({
path: `/articles/${edge.node.slug}`,
component: path.resolve("./src/templates/article-post.tsx"),
context: {
slug: edge.node.slug,
id: edge.node.id
},
})
})
}
我该何去何从?
首先,您需要为每个标记创建动态页面,以创建有效的链接元素。在gatsby-node.js
中,创建一个查询来获取所有标签,并为每个标签创建页面,如:
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const response = await graphql(`
query {
allContentfulArticlePost {
edges {
node {
id
slug
}
}
}
}
`)
response.data.allContentfulArticlePost.edges.forEach(edge => {
createPage({
path: `/articles/${edge.node.slug}`,
component: path.resolve("./src/templates/article-post.tsx"),
context: {
slug: edge.node.slug,
id: edge.node.id
},
})
})
const tags= await graphql(`
query {
allContentfulArticlePost {
edges {
node {
tags
}
}
}
}
`)
tags.data.allContentfulArticlePost.edges.forEach(edge=> {
let slugifiedTag= edges.node.tag.toLowerCase().replace("/^s+$/g", "-");
createPage({
path: `/tag/${slugifiedTag}`,
component: path.resolve("./src/templates/tag-post.tsx"), // your tagComponent
context: {
slug: edge.node.slug,
tagName: edges.node.tag
},
})
})
}
一步一步地,首先,您需要从tags
查询中的每个博客中检索所有标签。
然后,对于每个标签,您需要根据名称创建一个有效的段塞(即:This Is a Sample Tag
将在示例中转换为this-is-a-sample-tag
、slugifiedTag
(。这在edges.node.tag.toLowerCase().replace("/^s+$/g", "-")
中完成,正则表达式将全局匹配所有空格,并用连字符replace("/^s+$/g", "-")
替换它们。您可能需要解析tags
边以删除重复项,以避免创建重复项,创建Set
应该很适合您。
此时,您已经在/tag/${slugifiedTag
}(即:/tag/this-is-a-sample-tag
(下创建了所有页面。因此,您需要更改article-post.tsx
以指向标签页:
<Tags>
{props.data.contentfulArticlePost.tags.map(tag => {
let slugifiedTag= edges.node.tag.toLowerCase().replace("/^s+$/g", "-");
return <Link className="tag" key={tag} to={slugifiedTag}>
{tag}
</Link>
})}
</Tags>
请注意,您正在重复slugifiedTag
函数。您可以通过在CMS中创建一个标记实体并添加name
和slug
值来避免这种情况。如果在gatsby-node.js
查询和模板查询中检索段塞,则可以直接指向<Link className="tag" key={tag} to={tag.slug}>
。在此示例之后,name
将是This is a Sample Tag
,而slug
将是直接this-is-a-sample-tag
。
最后一步是在tag-post.tsx
中创建一个查询,获取每个标签的所有帖子,因为您通过上下文传递slug
和tagName
。您的查询应该如下所示:
export const query = graphql`
query($slug: String!, $tags: [String!]) {
contentfulArticlePost(slug: { eq: $slug }, tags: { in: $tags}) {
title
tags
publishedDate(formatString: "Do MMMM, YYYY")
featuredImage {
fluid(maxWidth: 720) {
...GatsbyContentfulFluid
}
}
body {
json
}
}
}
`
由于$tags
是一个数组,因此应声明为[String!]
。如果要使字段不可为null,只需添加感叹号(!
(,如[String!]!
。然后,您只需要筛选至少包含一个标记的by标记:tags: { in: $tags})
。
正如我所说,应该通过在CMS中添加一个带有name
和slug
字段的标记实体来改进和简化这一点。
在不了解数据结构和内部组件的情况下,这是一个宽泛的问题,但您了解了该方法的主要思想。