为 Gatsby Contentful 博客添加标签

IT技术 reactjs tags gatsby blogs contentful
2021-05-26 15:00:38

我正在尝试在博客文章上添加标签,但我很难找到解释如何实现它们的资源。

最终目标是获得可点击的标签,这会导致一个页面,其中所有具有相同标签的帖子都出现在一个列表中。

我将 GatsbyJS 与 Contentful 集成一起使用。

我有一个名为 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
      },
    })
  })
}

我从这里去哪里?

1个回答

首先,您需要为每个标签创建动态页面以创建有效的链接元素。在您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查询中的每个博客中检索所有标签

然后,对于每个标签,您需要根据名称创建一个有效的 slug(即:在示例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 中创建标签实体并添加 anameslug来避免这种情况如果您在gatsby-node.js查询以及模板查询中检索 slug ,则可以直接指向<Link className="tag" key={tag} to={tag.slug}>. 按照这个例子,name将是This is a Sample Tagslug将是直接的this-is-a-sample-tag

您最后要做的是在您的查询中创建一个查询,该查询tag-post.tsx获取每个标签的所有帖子,因为您是通过上下文传递slugtagName您的查询应如下所示:

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!]如果您想让该字段不可为空,只需添加感叹号 ( !),如[String!]!. 那么你只需要过滤包含至少一个标签的 by 标签:tags: { in: $tags})

正如我所说,这应该通过在你的 CMS 中添加一个标签实体来改进和简化,带有nameslug字段。

这是一个广泛的问题,无需了解您的数据结构和内部组件,但您已了解该方法的主要思想。