You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Gatsby中的嵌套节点创建Image数据?

问题

我尝试使用createRemoteFileNode为Product节点下的items数组中的节点创建优化图片。我有一个包含items的Product,每个Item都有featuredImg。我可以为Product创建featuredImg,但为子节点(Item)创建时,通过Products.items[i].featuredImg查询始终返回null,不过单独查询item.featuredImg时能正常返回gatsbyImageData。

我的节点创建代码如下:

const products = [
  {
    id: "product_1",
    imageUrl: "https://images.unsplash.com/photo-1665081661649-8656335a6cbb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1738&q=80",
    items: [
      {
        id: 'item_1',
        imageUrl: "https://images.unsplash.com/photo-1666120565124-7e763880444a?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80"
      }
    ]
  }
]

const sourceNodes = async ({ actions, createNodeId, createContentDigest }, options) => {
  
  products.forEach((testNode) => {
    const node = {
      ...testNode,
      id: createNodeId(`Product-${testNode.id}`),
    }

    actions.createNode({
      ...node,
      internal: {
        type: 'Product',
        contentDigest: createContentDigest(node),
      },
    });

    testNode.items.forEach(item => {

      const itemNode = {
        ...item,
        id: createNodeId(`Item-${item.id}`),
      }
  
      actions.createNode({
        ...itemNode,
        parent: node.id,
        internal: {
          type: 'Item',
          contentDigest: createContentDigest(itemNode),
        },
      });

    })
  })
};

module.exports = sourceNodes;

节点创建时执行的onCreateNode函数:

const { createRemoteFileNode } = require(`gatsby-source-filesystem`);

const onCreateNode = async ({ node, cache, store, getCache, actions: { createNode, createNodeField }, createNodeId }) => {  
  
  if( node.internal.type === 'Item') {
    
    const fileNode = await createRemoteFileNode({
      url: node.imageUrl,
      parentNodeId: node.parent,
      createNode,
      createNodeId,
      getCache,
    })

    if (fileNode) {
      createNodeField({ node, name: "localFile", value: fileNode.id })
    }
  }
  
  if( node.internal.type === 'Product') {
    
    const fileNode = await createRemoteFileNode({
      url: node.imageUrl,
      parentNodeId: node.id,
      createNode,
      createNodeId,
      getCache,
    })

    if (fileNode) {
      createNodeField({ node, name: "localFile", value: fileNode.id })
    }
  }
};

module.exports = onCreateNode

类型定义:

module.exports = ({ actions }) => {
  const { createTypes } = actions;
  const typeDefs = `
    type Product implements Node {
      id: String!
      imageUrl: String!
      featuredImg: File @link(from: "fields.localFile")
      items: [Item]
    }

    type Item implements Node {
      id: String!
      imageUrl: String!
      featuredImg: File @link(from: "fields.localFile")
    }
  `;
  createTypes(typeDefs);
};

我已找到类似问题,但不知如何将createSchemaCustomization或createResolvers解决方案应用到我的场景中。

解决方案

问题根源在于创建Product节点时,items字段存储的是原始对象数组,而非Gatsby节点引用。Gatsby无法自动关联这些原始对象和后续创建的Item节点,导致嵌套查询时无法找到对应的featuredImg。

方法一:修改节点创建逻辑,通过ID关联父子节点

  1. 修改sourceNodes函数,创建Product时不再保留原始items数组,而是存储子Item节点的ID列表:
const sourceNodes = async ({ actions, createNodeId, createContentDigest }, options) => {
  
  products.forEach((testNode) => {
    // 提取item的ID,后续用来关联
    const itemIds = testNode.items.map(item => createNodeId(`Item-${item.id}`));
    
    const node = {
      ...testNode,
      id: createNodeId(`Product-${testNode.id}`),
      // 替换原始items为ID数组
      itemIds: itemIds,
      // 删除原始items字段,避免冲突
      items: undefined,
    }

    actions.createNode({
      ...node,
      internal: {
        type: 'Product',
        contentDigest: createContentDigest(node),
      },
    });

    testNode.items.forEach(item => {
      const itemNode = {
        ...item,
        id: createNodeId(`Item-${item.id}`),
      }
  
      actions.createNode({
        ...itemNode,
        parent: node.id,
        internal: {
          type: 'Item',
          contentDigest: createContentDigest(itemNode),
        },
      });
    })
  })
};
  1. 更新类型定义,用@link通过itemIds关联到Item节点:
module.exports = ({ actions }) => {
  const { createTypes } = actions;
  const typeDefs = `
    type Product implements Node {
      id: String!
      imageUrl: String!
      featuredImg: File @link(from: "fields.localFile")
      itemIds: [String!]!
      items: [Item] @link(from: "itemIds")
    }

    type Item implements Node {
      id: String!
      imageUrl: String!
      featuredImg: File @link(from: "fields.localFile")
    }
  `;
  createTypes(typeDefs);
};

方法二:使用createResolvers手动解析Product的items字段

如果不想修改节点创建逻辑,可以通过createResolvers动态获取Product对应的子Item节点:

在gatsby-node.js中添加以下代码:

exports.createResolvers = ({ createResolvers }) => {
  const resolvers = {
    Product: {
      items: {
        resolve: (source, args, context, info) => {
          // 查找所有父节点为当前Product的Item节点
          return context.nodeModel.runQuery({
            query: {
              filter: { parent: { eq: source.id } }
            },
            type: "Item",
            firstOnly: false,
          })
        }
      }
    }
  }
  createResolvers(resolvers)
}

这个方法会在查询Product.items时,自动根据父子节点的parent关联关系返回对应的Item节点,嵌套查询items.featuredImg即可正常获取数据。

两种方法任选其一即可解决问题,推荐方法一,逻辑更清晰,性能更优。

内容的提问来源于stack exchange,提问作者Stretch0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 18:55:35