使用 graphql 在 Gatsby 中显示目录(降价文件)的内容

Posted

技术标签:

【中文标题】使用 graphql 在 Gatsby 中显示目录(降价文件)的内容【英文标题】:Displaying contents of directory(markdown files) in Gatsby using graphql 【发布时间】:2019-06-29 19:25:04 【问题描述】:

我正在使用 Gatsby/Netlify CMS 堆栈,并一直在尝试在主页上显示 markdown 文件内容。例如,我在 src/pages/experience 中有一个目录,显示了所有的体验降价文件。

所以使用graphql,我有一个实际有效的查询:


        allMarkdownRemark(
         limit: 3,
         sort:  order: DESC, fields: [frontmatter___date] ,
         filter:  fileAbsolutePath:  regex: "/(experience)/"  
       ) 
           edges 
             node 
               id
               frontmatter 
                 title
                 company_role
                 location
                 work_from
                 work_to
                 tags
               
               excerpt
             
           
         
     

但是,在我的组件页面上运行它时,它会显示 × TypeError:无法读取未定义的属性“allMarkdownRemark”

但是在输入这个之前返回:

if (!data)  return null ;

错误消失了,但整个部分都消失了。如下:

const Experience = (data) => 

return (

    <div id="experience" className="section accent">
              <div className="w-container">
                  <div className="section-title-group">
                    <Link to="#experience"><h2 className="section-heading centered">Experience</h2></Link>
                  </div>
              <div className="columns w-row">
                     data.allMarkdownRemark.edges.map((node) => (
                        <div className="column-2 w-col w-col-4 w-col-stack" key=node.id>
                            <div className="text-block"><strong>node.frontmatter.title</strong></div>
                            <div className="text-block-4">node.frontmatter.company_role</div>
                            <div className="text-block-4">node.frontmatter.location</div>
                            <div className="text-block-3">node.frontmatter.work_from – node.frontmatter.work_to</div>
                            <p className="paragraph">node.frontmatter.excerpt</p>
                            <div className="skill-div">node.frontmatter.tags</div>
                        </div>
                     )) 
              </div>
          </div>
      </div>
)

export default Experience

在 gatsby-config-js 中,我添加了一个独立于 /src/posts 的 gatsby-source-filesystem 解析到 /src/pages,其中体验目录是 src/pages/experience。

更新:2019 年 2 月 7 日 这是 gatsby-config-js 文件:

module.exports = 
  siteMetadata: 
    title: `Howard Tibbs Portfolio`,
    description: `This is a barebones template for my portfolio site`,
    author: `Howard Tibbs III`,
    createdAt: 2019
  ,
    plugins: [
      `gatsby-plugin-react-helmet`,
      
        resolve: `gatsby-source-filesystem`,
        options: 
          name: `images`,
          path: `$__dirname/src/images`,
          ,
      ,
      
        resolve: 'gatsby-transformer-remark',
        options: 
          plugins: [
            
              resolve: 'gatsby-remark-images',
            ,
          ],
        ,
      ,
      
        resolve: `gatsby-source-filesystem`,
        options: 
          name: `posts`,
          path: `$__dirname/src/posts`,
          ,
      ,
      
        resolve: `gatsby-source-filesystem`,
        options: 
          name: `pages`,
          path: `$__dirname/src/pages`,
          ,
      ,
        `gatsby-plugin-netlify-cms`,
        `gatsby-plugin-sharp`,
        
          resolve: `gatsby-plugin-manifest`,
          options: 
            name: `gatsby-starter-default`,
            short_name: `starter`,
            start_url: `/`,
            background_color: `#663399`,
            theme_color: `#663399`,
            display: `minimal-ui`,
            icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
          ,
        ,
        `gatsby-transformer-sharp`
    ],
  

我的感觉是,在 gatsby-node-js 的某个地方,我没有创建一个实例来处理该类型查询。

const path = require('path')
const  createFilePath  = require('gatsby-source-filesystem')

const PostTemplate = path.resolve('./src/templates/post-template.js')
const BlogTemplate = path.resolve('./src/templates/blog-template.js')

exports.onCreateNode = ( node, getNode, actions ) => 
    const  createNodeField  = actions
    if (node.internal.type === 'MarkdownRemark') 
        const slug = createFilePath( node, getNode, basePath: 'posts' )
    createNodeField(
        node,
        name: 'slug',
        value: slug,
    )




exports.createPages = async ( graphql, actions ) => 

const  createPage  = actions
const result = await graphql(`
    
        allMarkdownRemark (limit: 1000) 
          edges 
            node 
              fields 
                slug
              

            
          
        
      
`)

const posts = result.data.allMarkdownRemark.edges

posts.forEach(( node: post ) => 
    createPage(
        path: `posts$post.fields.slug`,
        component: PostTemplate,
        context: 
            slug: post.fields.slug,
        ,
    )
)

const postsPerPage = 2
const totalPages = Math.ceil(posts.length / postsPerPage)

Array.from( length: totalPages ).forEach((_, index) => 
    const currentPage = index + 1
    const isFirstPage = index === 0
    const isLastPage = currentPage === totalPages

    createPage(
        path: isFirstPage ? '/blog' : `/blog/$currentPage`,
        component: BlogTemplate,
        context: 
            limit: postsPerPage,
            skip: index * postsPerPage,
            isFirstPage,
            isLastPage,
            currentPage,
            totalPages,
        ,
    )
)

想知道是否有人能够得到类似工作的东西?非常感谢您的帮助。


更新:2019 年 2 月 6 日

所以对我的代码从 pageQuery 到 StaticQuery 进行了一些更改,不幸的是它仍然无法正常工作,但我相信它正在朝着正确的方向发展:

export default() => (

    <div id="experience" className="section accent">
              <div className="w-container">
                  <div className="section-title-group">
                    <Link to="#experience"><h2 className="section-heading centered">Experience</h2></Link>
                  </div>
              <div className="columns w-row">
              <StaticQuery
              query=graphql`
                  query ExperienceQuery 
                      allMarkdownRemark(
                       limit: 2,
                       sort:  order: DESC, fields: [frontmatter___date],
                       filter: fileAbsolutePath: regex: "/(experience)/"
                     ) 
                         edges 
                           node 
                             id
                             frontmatter 
                               title
                               company_role
                               location
                               work_from
                               work_to
                               tags
                             
                             excerpt
                           
                         
                       
                   

              `
              render=data => (
                  <div className="column-2 w-col w-col-4 w-col-stack" key=data.allMarkdownRemark.id>
                  <div className="text-block"><strong>data.allMarkdownRemark.frontmatter.title</strong></div>
                  <div className="text-block-4">data.allMarkdownRemark.frontmatter.company_role</div>
                  <div className="text-block-4">data.allMarkdownRemark.frontmatter.location</div>
                  <div className="text-block-3">data.allMarkdownRemark.frontmatter.work_from – data.allMarkdownRemark.frontmatter.work_to</div>
                  <p className="paragraph">data.allMarkdownRemark.frontmatter.excerpt</p>
                  <div className="skill-div">data.allMarkdownRemark.frontmatter.tags</div>
                  </div>
              )
              />
              </div>
          </div>
      </div>
);

我收到此错误 TypeError: Cannot read property 'title' of undefined

所以我想要完成的是本节中的这个实例。当然这是一个占位符,但我希望用每个降价的内容替换该占位符。 Experience snip


更新:2019 年 2 月 7 日

所以今天没有更改,但想发布一些字段以更好地了解我正在尝试做的事情。这是来自 NetlifyCMS 的 config.yml 文件,它在其中显示集合。这就是我正在完成的(注意:测试 repo 只是为了查看实际的 CMS,我会寻求更改):

backend:
  name: test-repo
  branch: master

media_folder: static/images
public_folder: /images

display_url: https://gatsby-netlify-cms-example.netlify.com/

# This line should *not* be indented
publish_mode: editorial_workflow

collections:

  - name: "experience"
    label: "Experience"
    folder: "experience"
    create: true
    fields:
        -  name: "title", label: "Company Title", widget: "string" 
        -  name: "company_role", label: "Position Title", widget: "string" 
        -  name: "location", label: "Location", widget: "string" 
        -  name: "work_from", label: "From", widget: "date", format: "MMM YYYY" 
        -  name: "work_to", label: "To", default: "Present", widget: "date", format: "MMM YYYY" 
        -  name: "description", label: "Description", widget: "text" 
        -  name: "tags", label: "Skills Tags", widget: "select", multiple: "true", 
              options: ["ReactJS", "NodeJS", "html", "CSS", "Sass", "php", "Typescript", "Joomla", "CMS Made Simple"] 


  - name: "blog"
    label: "Blog"
    folder: "blog"
    create: true
    slug: "year-month-day_slug"
    fields:
      -  name: path, label: Path 
      -  label: "Image", name: "image", widget: "image" 
      -  name: title, label: Title 
      -  label: "Publish Date", name: "date", widget: "datetime" 
      - label: "Category", name: "category", widget: "string"
      -  name: "body", label: "body", widget: markdown 
      -  name: tags, label: Tags, widget: list 


  - name: "projects"
    label: "Projects"
    folder: "projects"
    create: true
    fields:
      -  name: date, label: Date, widget: date 
      - label: "Category", name: "category", widget: "string"
      -  name: title, label: Title 
      -  label: "Image", name: "image", widget: "image" 
      -  label: "Description", name: "description", widget: "text" 
      -  name: body, label: "Details", widget: markdown 
      -  name: tags, label: Tags, widget: list


  - name: "about"
    label: "About"
    folder: "src/pages/about"
    create: false
    slug: "slug"
    fields:
      - 
          label: "Content Type",
          name: "contentType",
          widget: "hidden",
          default: "about",
        
      -  label: "Path", name: "path", widget: "hidden", default: "/about" 
      -  label: "Title", name: "title", widget: "string" 
      -  label: "Body", name: "body", widget: "markdown" 

对于降价页面的示例,这将是在“体验”部分中查找的格式,因为正如您在图片中看到的那样,它显示在整个容器中:

---
title: Test Company
company_role: Test Role
location: Anytown, USA
work_from: January, 2020
work_to: January, 2020
tags: Test, Customer Service
---

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.

更新:2019 年 2 月 8 日

我确实对下面提供的代码进行了一些更新,但在我开始之前,这里有一些我想要完成的图像。这些是我希望替换为真实数据的占位符。这是针对每个部分的:

Full Experience snip

Projects snip

Blog snip

我在下面的答案中运行了@staypuftman 提供的代码并出现了这个错误:

您网站的“gatsby-node.js”创建了一个页面,其中包含一个组件 不存在。

除了已经存在的代码之外,我还添加了代码并处理了该错误。这就是我最初认为会发生的事情,也是我想独立使用 StaticQuery 的原因。这实际上是我在文档和入门存储库中遇到的主要问题,没有人真正在 node.js 中创建多个变量。

我还尝试了来自@DerekNguyen 的修订版,看起来像这样:

import React from "react"
import  Link, graphql, StaticQuery  from "gatsby"



export default(data) => (

        <div id="experience" className="section accent">
                  <div className="w-container">
                      <div className="section-title-group">
                        <Link to="#experience"><h2 className="section-heading centered">Experience</h2></Link>
                      </div>
                  <div className="columns w-row">
                  <StaticQuery
                  query=graphql`
                      query ExperienceQuery 
                          allMarkdownRemark(
                           limit: 2,
                           sort:  order: DESC, fields: [frontmatter___date],
                           filter: fileAbsolutePath: regex: "/(experience)/"
                         ) 
                             edges 
                               node 
                                 id
                                 frontmatter 
                                   title
                                   company_role
                                   location
                                   work_from
                                   work_to
                                   tags
                                 
                                 excerpt
                               
                             
                           
                       

                  `
                  render=data.allMarkdownRemark.edges.map(( node ) => (
                      <div className="column-2 w-col w-col-4 w-col-stack" key=node.id>
                      <div className="text-block"><strong>node.frontmatter.title</strong></div>
                      <div className="text-block-4">node.frontmatter.company_role</div>
                      <div className="text-block-4">node.frontmatter.location</div>
                      <div className="text-block-3">node.frontmatter.work_from – node.frontmatter.work_to</div>
                      <p className="paragraph">node.frontmatter.excerpt</p>
                      <div className="skill-div">node.frontmatter.tags</div>
                      </div>
                  ))
                  />
                  </div>
              </div>
          </div>
);

但这也带来了错误:

TypeError:无法读取未定义的属性“边缘”

仍在努力,但我认为它越来越接近解决方案。请记住,我还必须为其他变量创建它。


更新:2019 年 2 月 10 日

对于那些想看看我是如何使用 gatsby-starter 构建网站的人,这里是:

My portfolio

【问题讨论】:

此体验组件在您的 src 中的什么位置?会不会是你在非页面组件中使用了页面查询? 因此 experience.js 组件位于不同的目录中。在 src/components/Experience 中。我将主页的每个部分分成子组件,它们转到各自的目录。然后,如果我需要将特定页面添加到该部分,它会转到该目录。 那么您将无法导出 graphql 查询并在 props 中期望 data,gatsby 将忽略您的查询!你必须改用StaticQuery 我已经在我的实际代码中注释掉了 StaticQuery。如果这可行,我非常感谢@DerekNguyen 它对我不起作用,我必须遗漏一些东西。我会继续使用 StaticQuery,但缺少一些东西。 【参考方案1】:

gastby-node.js 用于当您有一堆页面需要位于/pages/variable-here/ 时。 Gatsby 使用gatsby-node.js 对您的数据源(在本例中为 Netlify CMS)运行 GraphQL 查询,并根据您的特定 GraphQL 查询获取所需的所有内容。

然后它会使用项目中的组件动态构建 X 个页面。它构建的页面数量取决于它在远程数据源中找到的内容。它们的外观取决于您指定的组件。在Gatsby tutorial 中阅读更多相关信息。

Staticquery 用于将一次性数据获取到组件中,而不是从数据源生成页面。它非常有用,但不是我认为您正在尝试做的事情。在Gatsby site 上阅读更多相关信息。

根据所有这些以及您在上面提供的内容,我认为您的 gatsby-node.js 应该是这样的:

// Give Node access to path
const path = require('path')

// Leverages node's createPages capabilities
exports.createPages = async ( graphql, actions ) => 

  // Destructures createPage from redux actions, you'll use this in a minute
  const  createPage  = actions

  // Make your query
  const allExperiencePages = await graphql(`
     
      allMarkdownRemark(limit: 1000) 
        edges 
          node 
            id
            frontmatter 
              title
              company_role
              location
              work_from
              work_to
              tags
            
            excerpt
            
        
      
    
  `)

  // gatsby structures allExperiencePages into an object you can loop through
  // The documentation isn't great but the 'data' property contains what you're looking for
  // Run a forEach with a callback parameter that contains each page's data
  allExperiencePages.data.allMarkdownRemark.edges.forEach( page => 

    // Make individual pages with createPage variable you made earlier
    // The 'path' needs to match where you want the pages to be when your site builds
    // 'conponent' is what Gatsby will use to build the page
    // 'context' is the data that the component will receive when you `gatsby build`
    createPage(
      path: `/pages/$page.node.title/`,
      component: path.resolve('src/components/Experience'),
      context: 
        id: page.node.id,
        title: page.node.frontmatter.title,
        company_role: page.node.frontmatter.company_role,
        location: page.node.frontmatter.location,
        work_from: page.node.frontmatter.work_from,
        work_to: page.node.frontmatter.work_to,
        tags: page.node.frontmatter.tags,
        excerpt: page.node.excerpt
      
    )
  ) 


仅此一项可能还不足以生成页面!这完全取决于您在gatsby-node.js 文件的createPage 组件部分中指定的组件的情况。

【讨论】:

非常感谢您的回答。我会在最后尝试一下。我相信我避免为体验降价页面制作页面。我认为您可以在组件中创建所需字段的实例,而无需从该特定文件创建页面。我会解决这个问题,让你知道我得到了什么。 另外,这就是我这样做的原因,还有一个项目页面和博客页面,每个部分都显示主页上的前 3 个最新实例。有了上面的内容,我会以同样的方式为 allProjects 和 allBlogs 创建一个 const 吗? 从 OP 的问题来看,他似乎希望拥有一个独立的 Experience 组件,然后可以将其放置在其他页面或发布模板中,而不是创建单独的体验页面。 @H.Tibbs 对吗? @DerekNguyen 正确。所以我需要这些组件来填充新实例:体验、项目和博客。每个都会在主页上显示前 3 个最近的实例(例如我最近的经历、最近的项目等),但是当您单击部分文本或查看更多(考虑添加该行动呼吁)时,它将按降序显示该组件的整个列表。因此,就像一个列出所有博客的博客页面,然后点击每个博客文章。 此外,体验条目不必是单独的帖子,但为了简单起见,它可以是相同类型的降价。

以上是关于使用 graphql 在 Gatsby 中显示目录(降价文件)的内容的主要内容,如果未能解决你的问题,请参考以下文章

使用 gatsby-source-graphql 时,GraphIQL 资源管理器未在 Gatsby 中显示

gatsby-source-graphql + ACF 字段未显示

在 Gatsby 中使用 graphQL 查询多个 JSON 文件

使用 Gatsby Image 和 Graphql 显示图像列表

gatsby-source-drupal 不显示 graphql 查询

在 Gatsby 中使用 Graphql 和 Contentful 时图像不显示,道具类型失败:类型为“数字”的道具“图像”无效,预期为“对象”?