React - 动态导入组件
React - Dynamically Import Components
我有一个页面可以根据用户输入呈现不同的组件。目前,我已经对每个组件的导入进行了硬编码,如下所示:
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
不过,我一直change/add组件。有没有办法让一个文件只存储组件的名称和路径,然后将这些动态导入另一个文件?
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
您可以使用条件渲染代替。希望对你有帮助
您可以创建一个利用 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>
)
}
}
我认为对于我想要实现的目标可能存在一些混淆。我设法解决了我遇到的问题,并在下面显示了我的代码,其中显示了我是如何解决它的。
单独的文件(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
这种方法允许我非常快速地 add/remove 个组件,因为导入在一个文件中并且一次只需要更改一行。
这是另一个解决方案:
我们得到了所需组件的列表 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导入它们,但是模块已经是promises了。
以防我们最近需要从服务器 ajax 它们,所以我们需要在使用 require(或类似的东西)捆绑之前拆分模块.
您可以将您的组件捆绑为 micro-apps 并从 url 将它们热加载到您的应用程序中。这是一个支持从基于站点级别配置的路由动态导入组件和micro-apps的poc。
由于问题真的很老,答案可能还不错。但是现在,如果有人有同样的问题应该使用动态导入,以便只加载需要的组件,避免加载所有不同的组件。
const component = React.lazy(() => import('./component.jsx'));
试试这里的例子:demo
另一种无需任何承诺即可进行动态导入的方法:
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 将 classes 编译为另一个名称,例如 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
};
您可以照常导入它 class 姓名:
new (setClasses[`ColumnSet${n}Brick`])(parameters)
您可以使用 '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 /></>
); }
如果我们需要使用动态导入,为了只加载需要的组件,避免加载所有不同的组件。使用代码拆分
(async () => {
const { Component1 } = await import('./Component1');
})();
我有一个页面可以根据用户输入呈现不同的组件。目前,我已经对每个组件的导入进行了硬编码,如下所示:
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
不过,我一直change/add组件。有没有办法让一个文件只存储组件的名称和路径,然后将这些动态导入另一个文件?
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
您可以使用条件渲染代替。希望对你有帮助
您可以创建一个利用 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>
)
}
}
我认为对于我想要实现的目标可能存在一些混淆。我设法解决了我遇到的问题,并在下面显示了我的代码,其中显示了我是如何解决它的。
单独的文件(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
这种方法允许我非常快速地 add/remove 个组件,因为导入在一个文件中并且一次只需要更改一行。
这是另一个解决方案:
我们得到了所需组件的列表 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导入它们,但是模块已经是promises了。
以防我们最近需要从服务器 ajax 它们,所以我们需要在使用 require(或类似的东西)捆绑之前拆分模块.
您可以将您的组件捆绑为 micro-apps 并从 url 将它们热加载到您的应用程序中。这是一个支持从基于站点级别配置的路由动态导入组件和micro-apps的poc。
由于问题真的很老,答案可能还不错。但是现在,如果有人有同样的问题应该使用动态导入,以便只加载需要的组件,避免加载所有不同的组件。
const component = React.lazy(() => import('./component.jsx'));
试试这里的例子:demo
另一种无需任何承诺即可进行动态导入的方法:
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 将 classes 编译为另一个名称,例如 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
};
您可以照常导入它 class 姓名:
new (setClasses[`ColumnSet${n}Brick`])(parameters)
您可以使用 '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 /></>
); }
如果我们需要使用动态导入,为了只加载需要的组件,避免加载所有不同的组件。使用代码拆分
(async () => {
const { Component1 } = await import('./Component1');
})();