【问题标题】:React - Dynamically Import ComponentsReact - 动态导入组件
【发布时间】:2018-06-24 09:49:54
【问题描述】:

我有一个页面,它根据用户输入呈现不同的组件。目前,我已经对每个组件的导入进行了硬编码,如下所示:

    import React, { Component } from 'react'
    import Component1 from './Component1'
    import Component2 from './Component2'
    import Component3 from './Component3'

    class Main extends Component {
        render() {
            var components = {
                'Component1': Component1,
                'Component2': Component2,
                'Component3': Component3
            };
            var type = 'Component1';  // just an example
            var MyComponent = Components[type];
            return <MyComponent />
        }
    }

    export default Main

但是,我一直在更改/添加组件。有没有办法让一个文件只存储组件的名称和路径,然后将它们动态导入另一个文件?

【问题讨论】:

    标签: javascript reactjs import components react-component


    【解决方案1】:

    我认为我想要达到的目标可能有些混乱。我设法解决了我遇到的问题,并在下面显示了我的代码,说明了我是如何解决的。

    单独的文件(ComponentIndex.js):

        let Components = {};
    
        Components['Component1'] = require('./Component1').default;
        Components['Component2'] = require('./Component2').default;
        Components['Component3'] = require('./Component3').default;
    
        export default Components
    

    主文件(Main.js):

        import React, { Component } from 'react';
        import Components from './ComponentIndex';
    
        class Main extends Component {
            render () {
                var type = 'Component1'; // example variable - will change from user input
                const ComponentToRender = Components[type];
                return <ComponentToRender/>
            }
        }
    
        export default Main
    

    此方法允许我非常快速地添加/删除组件,因为导入位于一个文件中,并且一次只需要更改一行。

    【讨论】:

    • 我们的目标是加载新组件,尽可能少地更改代码。这种方法只需要添加一行。
    • 你也可以先export { default as Component1 } from './Component1' 然后import * as componentList from './ComponentIndex' 然后componentList[type]
    • 为什么我们再次使用.default 访问
    • 非常感谢,这对我的问题最有效。我试图根据配置文件渲染不同的组件,这是动态的。
    • 你会如何在打字稿中做到这一点?我有这个错误=> Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ ComponentCar: () =&gt; Element; }'. No index signature with a parameter of type 'string' was found on type '{ ComponentCar: () =&gt; Element; }'.ts(7053) 这来自这个命令const DynamicComponent = Components[Component] 其中ComponentCar
    【解决方案2】:

    由于问题真的很老,答案可能还可以。但是现在,如果有人遇到同样的问题,应该使用动态导入,以便只加载需要的组件,避免加载所有不同的组件。

    const component = React.lazy(() => import('./component.jsx'));
    

    在此处尝试示例:demo

    【讨论】:

    【解决方案3】:
    import React, { Component } from 'react'
    import Component1 from './Component1'
    import Component2 from './Component2'
    import Component3 from './Component3'
    
    class Main extends Component {
        render() {
            var type = 'Component1';  // just an example
            return (
              <div>
                {type == "Component1" && <Component1 />}
                {type == "Component2" && <Component2 />}
                ...
              </div>
            )
        }
    }
    
    export default Main
    

    您可以使用条件渲染 insted。希望对你有帮助

    Check this

    【讨论】:

    • 简单的方法!解决了我的问题。我试图在使用该组件时加载它。 const Modal = lazy(() => import('./sharedmodule/common-modal/common-modal.component'));上面的 Modal 块在 DOM 上渲染后立即被加载。因为我在做。 return( // 这导致它立即渲染)
    【解决方案4】:

    这是另一个解决方案: 我们得到所需组件的列表list = ['c1', 'c2', 'c3']。它可以从 json 文件中拉到一个数组中(我使用 redux-store,所以我通过 this.props.getForms() 开始获取表单)。但您可以手动创建和访问组件列表。

        componentDidMount = () => {
    //we get elements list from any source to redux-store
            this.props.getForms();
    //access redux-store to the list
            const forms = this.props.configBody.sets;
    //make deep object copy
            const updatedState = { ...this.state };
            updatedState.modules = [];
            if (forms) {
    //here is the very dynamic import magic: we map the import list and prepare to store the imports in Component`s state
                const importPromises = forms.map(p =>
                    import(`../TemplateOrders/Template${p.order}`)
                        .then(module => {
                            updatedState.modules.push(module.default)
                        })
                        .catch(errorHandler(p))
                )
    //wait till all imports are getting resolved
                Promise.all(importPromises)
                    .then(res =>
    //then run setState
                        this.setState({ ...updatedState }, () => {
                            console.log(this.state);
                        }))
            }
        }
    
        render() {
            const forms = this.props.configBody.sets;
    //we iterate through the modules and React.createElemet`s 
            const list = this.state.modules
                ? this.state.modules.map((e, i) =>
                    createElement(e, { key: forms[i].title }, null)
                )
                : [];
            return (
                <Fragment>
                    <Link to='/'>Home</Link>
                    <h1>hello there</h1>
    //push them all to get rendered as Components
                    {list.map(e => e)}
                </Fragment>
            )
        }
    

    因此,当您的应用加载时,它会拉取所需的模块。

    我想用 promises 来导入它们,但是模块已经是 promise。

    如果我们最近需要从服务器 ajax,所以我们需要在与 require 捆绑之前拆分模块(或类似的东西)不知道。

    【讨论】:

      【解决方案5】:

      您可以将您的组件捆绑为微应用程序,然后从一个 url 将它们热加载到您的应用程序中。这是一个支持从基于站点级别配置的路由动态导入组件和微应用的 poc。

      https://github.com/eschall/react-micro-frontend

      【讨论】:

      • 我查看了你的 poc,但由于我从未使用过 redux,所以我不太了解它。我想从外部 api 导入一个反应组件。结果可以进行预处理等。我只是想知道这是否可能。我确实在这里问过这个问题stackoverflow.com/q/59018834/6394630也许你可以看看它
      【解决方案6】:

      另一种在没有任何承诺的情况下进行动态导入的方法:

      import React from "react";
      import ColumnSet1Brick from "./sets/column-set/column-set-1-brick";
      import ColumnSet2Brick from "./sets/column-set/column-set-2-brick";
      import ColumnSet3Brick from "./sets/column-set/column-set-3-brick";
      import ColumnSet4Brick from "./sets/column-set/column-set-4-brick";
      
      const setClasses = {
        ColumnSet1Brick,
        ColumnSet2Brick,
        ColumnSet3Brick,
        ColumnSet4Brick
      };
      
      export default class SetBrickStack extends React.Component {
      
        ...
      
      
      
        getColumnSetInstance = (n) => new (setClasses[`ColumnSet${n}Brick`])(this.paramFactory.getBasicProps());
      
        getBricksOnInit = () => {
          const columnSets = [1, 2, 3, 4];
          const bricksParams = columnSets.map(this.getColumnSetInstance);
          return bricksParams;
        };
      }
      

      诀窍是 babel 将类编译为另一个名称,例如 react__WEBPACK_IMPORTED_MODULE_1___default 所以要访问它,我们需要在一个对象中分配编译模块名称,这就是为什么有 setClasses 编译为对象参考文献

      const setClasses = {
        ColumnSet1Brick: react__WEBPACK_IMPORTED_MODULE_1___default,
        ColumnSet2Brick: react__WEBPACK_IMPORTED_MODULE_2___default,
        ColumnSet3Brick: react__WEBPACK_IMPORTED_MODULE_3___default,
        ColumnSet4Brick: react__WEBPACK_IMPORTED_MODULE_4___default
      };
      

      你可以像往常的类名一样导入它:

      new (setClasses[`ColumnSet${n}Brick`])(parameters)
      

      【讨论】:

        【解决方案7】:

        您可以创建一个使用React.createElement 的组件构建函数。这样您就可以从帮助文件中导入函数。如果没有更多信息,很难在此示例中显示更多代码,但如果您的目标是完全删除此组件中的逻辑,您也可以使用此文件中的状态助手。

        class Main extends Component {
        
        constructor(props) {
          super();
          this.state = { displayComponent: Component1 }
        }
        
        buildComponent = () => {
          // create element takes additional params for props and children
          return React.createElement( this.state.displayComponent )
        }
        
        render() {
            var type = 'Component1';  // just an example
            return (
              <div>
                { this.buildComponent() }
              </div>
            )
        }
        

        }

        【讨论】:

          【解决方案8】:

          如果我们需要使用动态导入,为了只加载需要的组件而避免加载所有不同的组件。使用代码拆分

          (async () => {
            const { Component1 } = await import('./Component1');
          })();
          

          【讨论】:

            【解决方案9】:

            您可以使用 'react-router-dom' 中的 Route 和 Switch 来根据路径动态渲染组件。这是示例

            render() {
            return (
              <>
                <Header  />
                <BrowserRouter>
                  <Switch>
                    <Route path="/abc" exact render={() => (<Abc />)}/>
                    <Route path="/abcd" exact render={() => (<Abcd {...this.props} />)}/>
                    <Route path="/xyz" exact render={() => (<Xyz />)}/>
                    
                  </Switch>
                </BrowserRouter>
                <Footer /></>
            );  }
            

            【讨论】:

            • 选择性渲染是但不是动态导入:)
            猜你喜欢
            • 1970-01-01
            • 2020-03-02
            • 2017-04-19
            • 2021-08-13
            • 1970-01-01
            • 1970-01-01
            • 2021-05-03
            • 2020-10-18
            • 2019-09-15
            相关资源
            最近更新 更多