【问题标题】:How to use GraphQL fragment on multiple types如何在多种类型上使用 GraphQL 片段
【发布时间】:2018-12-18 12:46:05
【问题描述】:

我有一个 Gatsby 项目,它对两种不同类型的内容进行了非常相似的 GraphQL 查询:常规页面和 wiki 文章。

分页

export const query = graphql`
  query($slug: String!) {
    page: contentfulPage(slug: {eq: $slug}) {
      title
      slug
      body {
        remark: childMarkdownRemark {
          excerpt
          html
          headings {
            value
            depth
          }
        }
      }
      updatedAt(formatString: "D. MMM YYYY")
      authors {
        name
        email
      }
    }
  }
`

slug 的维基文章

export const query = graphql`
  query($slug: String!) {
    article: contentfulWikiArticle(slug: {eq: $slug}) {
      title
      slug
      body {
        remark: childMarkdownRemark {
          excerpt
          html
          headings {
            value
            depth
          }
        }
      }
      updatedAt(formatString: "D. MMM YYYY")
      authors {
        name
        email
      }
 +    section {
 +      title
 +      slug
 +    }
 +    subsection {
 +      title
 +      slug
 +    }
    }
  }
`

除了 wiki 文章的附加部分和小部分之外,查询是相同的。为了保持干燥,我如何将页面字段移动到一个单独的片段中,尽管它是不同类型的,但也可以传播到 wiki 文章查询中? GraphQL 能否提供类似的东西:

fragment pageFields on [ContenfulPage, ContenfulWikiArticle] {
  ...
}

【问题讨论】:

  • 好问题 我有类似的问题?你想清楚了吗?
  • @me-me 恐怕还没有。
  • 不确定您是否已解决,这很难解释,但如果我看到源代码,我将能够以格式良好的方式回答,但考虑从组件中的 layout.js 制作“TemplateWrapper 组件”文件夹并为您在组件中构建的 Contentful 字段创建一个“LayoutfFagment”,然后声明共享数据或重复为 null 的字段,然后导出查询并过滤共享数据或字段。在这之后还有几步,如果你仍然卡住,我会伸出援手。
  • @NickC 感谢您的评论,但我真正要问的是 GraphQL 是否提供了一种本地方式来执行此操作。我不太明白你在暗示什么,但听起来有点老套。
  • 使用 Apollo 将“本地”完成此操作

标签: graphql dry gatsby contentful


【解决方案1】:

Gatsby recent release允许用户为graphql schema设置自己的类型,让这个问题终于成为可能。

如果用户可以控制架构,那么使用 graphql 总是可能的,但是由于最近的 Gatsby 更新,用户终于可以自己实现这一点。

设置

为了建立一个简单的例子,我将在这样一个简单的文件夹上使用gatsby-transformer-json

jsonFolder
  |--one.json { "type": "One", "name": "a", "food": "pizza" }
  `--two.json { "type": "Two", "name": "b", "game": "chess" }

并使用选项声明我的类型名称:

{
  resolve: `gatsby-transformer-json`,
  options: { 
    typeName: ({ object }) => object.type,
  },
},

现在我有两种为我创建的类型。我可以在其中一个上创建片段,但不能同时在两者上创建:

export const name = graphql`
  fragment name on One {
    name
  }
`

export const pageQuery = graphql`
  query {
    one {
      ...name
    }
    two {
      ...name <-- ⚠️ throw type error
    }
  }
`

让我们解决这个问题。

设置类型

我将使用一个名为 createTypes 的新 API 来为每个 json 注册一个新接口和两种类型。注意JsonNode 包含OneTwo 的公共字段:

exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
    interface JsonNode {
      name: String
      type: String!
    }

    type One implements Node & JsonNode {
      name: String
      type: String!
      food: String
    }

    type Two implements Node & JsonNode {
      name: String
      type: String!
      game: String
    }
  `
  createTypes(typeDefs)
}

奇迹发生在这一行,OneTwo 实现了JsonNode(自定义接口)和Node(盖茨比的接口)。

type One implements Node & JsonNode { ... }

现在我可以编写一个实现JsonNode 的片段,它适用于这两种类型。

// blogPostTemplate.js
import React from "react"
import { graphql } from "gatsby"

export default ({ data }) => <div>{JSON.Stringify(data)}</div>

export const name = graphql`
  fragment name on JsonNode {
    name
    level
  }
`

export const pageQuery = graphql`
  query {
    one {
      ...name <- ? works
    }
    two {
      ...name <- ? works
    }
  }
`

这需要一些设置,但如果您提前知道您的数据类型并且需要大量重用片段,那么这可能是值得的。

【讨论】:

  • 非常酷!就像你说的,相当多的设置所以感谢这个详细的答案。
猜你喜欢
  • 2018-08-03
  • 2020-10-20
  • 2018-11-21
  • 1970-01-01
  • 2019-03-25
  • 2020-07-14
  • 2019-05-14
  • 1970-01-01
  • 2020-02-01
相关资源
最近更新 更多