如何从 JSON 文件在 Gatsby 中创建页面?

Posted

技术标签:

【中文标题】如何从 JSON 文件在 Gatsby 中创建页面?【英文标题】:How to create a page in Gatsby from JSON file? 【发布时间】:2019-12-07 09:45:30 【问题描述】:

我正在尝试使用 JSON 文件在 Gatsby 中动态创建一个页面。在该文件中,我定义了应在页面中呈现的组件。

我遵循了 Gatsby 的文档,但是,它没有我想要的。所以我尝试通过读取 JSON 文件并遍历其中的组件并使用 React.createElement() 创建它们来创建页面。最后,我得到了一个 react 组件数组,我将这些组件传递给了模板页面组件的 createPage 方法的 context 对象中的 children prop。

这是解决这个想法的正确方法吗?它在盖茨比中是否可行?

我认为说我尝试了动态导入并且效果很好是很有用的,但我正在尝试找到一种方法,我不必将所有组件转储到一个文件夹中。

我有这个项目的 Github 存储库。 https://github.com/ahmedalbeiruti/Gatsby-dynamic-pages

这里是代码的主要部分:

gatsby-node.js

exports.createPages = (actions)=>
    const createPage = actions
    const resutl = componentsRenderer(data.page.layout.columns)
    createPage(
        path: data.page.name,
        component: path.resolve('./src/template/page.js'),
        context:
            children: resutl
        
    )


const componentsRenderer = components => 

    return components.map(component => 
        let children = []
        if (component.children)
            children = componentsRenderer(component.children)
        
        const element = require(`$__dirname/$component.path`)

        return React.createElement(element, Object.assign(,key: component.key,...component.props),children)
    );


data/sample-page-no-props.json


        "page":
            "name": "about",
            "layout":
                "columns": [
                    
                        "key":"column_1",
                        "name": "Column",
                        "path": "/src/components/layouts/column.jsx",
                        "children":[
                            
                                "name": "FirstComponent",
                                "path": "/src/components/custom/first-component.jsx",
                                "key": "first_component_1"
                            
                        ]
                    ,
                    
                        "key": "column_2",
                        "name": "Column",
                        "path": "/src/components/layouts/column.jsx",
                        "children":[
                            
                                "key": "second_component_1",
                                "name": "SecondComponent",
                                "path": "/src/components/custom/second-component.jsx",
                                "children":[
                                    
                                        "key": "leaf_component_1",
                                        "name": "LeafComponent",
                                        "path":"/src/components/custom/leaf-component.jsx"
                                    
                                ]
                            ,
                            
                                "key": "third_component_1",
                                "name": "ThirdComponent",
                                "path": "/src/components/custom/third-component.jsx",
                                "children":[
                                    
                                        "key": "leaf_component_1",
                                        "name": "LeafComponent",
                                        "path":"/src/components/custom/leaf-component.jsx"
                                    
                                ]
                            
                        ]
                    
                ]
            
        


data/sample-page-with-style-prop.json(FirstComponent 有 props 对象)


        "page":
            "name": "about",
            "layout":
                "columns": [
                    
                        "key":"column_1",
                        "name": "Column",
                        "path": "/src/components/layouts/column.jsx",
                        "children":[
                            
                                "name": "FirstComponent",
                                "path": "/src/components/custom/first-component.jsx",
                                "key": "first_component_1",
                                "props":
                                    "style":
                                        "color":"red"
                                    
                                
                            
                        ]
                    ,
                    
                        "key": "column_2",
                        "name": "Column",
                        "path": "/src/components/layouts/column.jsx",
                        "children":[
                            
                                "key": "second_component_1",
                                "name": "SecondComponent",
                                "path": "/src/components/custom/second-component.jsx",
                                "children":[
                                    
                                        "key": "leaf_component_1",
                                        "name": "LeafComponent",
                                        "path":"/src/components/custom/leaf-component.jsx"
                                    
                                ]
                            ,
                            
                                "key": "third_component_1",
                                "name": "ThirdComponent",
                                "path": "/src/components/custom/third-component.jsx",
                                "children":[
                                    
                                        "key": "leaf_component_1",
                                        "name": "LeafComponent",
                                        "path":"/src/components/custom/leaf-component.jsx"
                                    
                                ]
                            
                        ]
                    
                ]
            
        


模板/page.js

import React from 'react'
import Layout from '../components/layouts/Layout'

const Page = (props)=>
    console.log(`page_context: $props.pageContext.children`)
    return (
        <>
        <h1>this is the about page</h1>
        <Layout>
            props.pageContext.children
        </Layout>
        </>
    )


export default Page

components/custom/first-component.jsx

// import React from "react";

const React = require("react");

module.exports = (props)=>
    return(
        <h3 style=props.style>
            Hi this is the first component
        </h3>
    )


// export default FirstComponent

使用 sample-page-with-style-prop.json 文件时遇到的错误如下:

UNHANDLED REJECTION Cannot assign to read only property 'style' of object '#<Object>'

TypeError: Cannot assign to read only property 'style' of object '#<Object>'

如果我更改为 sample-page-no-props.json 文件,我会收到以下错误:

UNHANDLED REJECTION Cannot assign to read only property 'children' of object '#<Object>'

TypeError: Cannot assign to read only property 'children' of object '#<Object>'

【问题讨论】:

【参考方案1】:

而不是将组件作为上下文传递给页面模板。正确的方法是传递数据并在 Page 中渲染组件

gatsby-node.js

exports.createPages = (actions)=>
    const createPage = actions
    const resutl = componentsRenderer(data.page.layout.columns)
    createPage(
        path: data.page.name,
        component: path.resolve('./src/template/page.js'),
        context:
            children: resutl
        
    )

template/page.js

import React from 'react'
import Layout from '../components/layouts/Layout'

const componentsRenderer = components => 

    return components.map(component => 
        let children = []
        if (component.children)
            children = componentsRenderer(component.children)
        
        const element = require(`$HOME_DIR/$component.path`)

        return React.createElement(element, Object.assign(,key: component.key,...component.props),children)
    );

const Page = (props)=>
    const data = props.pageContext.children
    return (
        <>
        <h1>this is the about page</h1>
        <Layout>
            componentsRenderer(data)
        </Layout>
        </>
    )


export default Page

PS:确保页面组件中HOME_DIR路径正确。

【讨论】:

非常感谢您的回复,我正在尝试您的建议。不幸的是,它没有用。我收到错误:元素类型无效:需要一个字符串(对于内置组件)或一个类/函数(对于复合组件),但得到:对象。这可能是因为我没有将要求更改为 es6 导入(实际上是使用可加载组件),但这将使它成为我已经完成的动态导入并且它工作正常。我正在寻找替代方案,因为我的团队选择不进行动态导入。 似乎您应该将组件导出为默认值并导入为默认值,如const element = require(`$HOME_DIR/$component.path`).default

以上是关于如何从 JSON 文件在 Gatsby 中创建页面?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Gatsby 中为我的可重用组件返回特定图像?

如何从DataFrame在谷歌存储中创建一个Json文件?

如何在反应钩子中创建一个新的 JSON 对象?

Gatsby:在页面上组合两个 graphql 源(.json 和 .jpg 源)

如何在 php 中创建类似 twitter 的 search.json

如何从 JSON 数组在 DB 中创建表以在 Spring Boot 中创建 REST API