[英]Dynamic loading of react components
我需要动态加载反应组件。
我从用户那里获取要作为字符串加载的组件的名称。 我正在使用webpack 。
如何动态加载组件而不是静态导入语句。 似乎Require.Ensure
不评估表达式。 我想要实现的是这样的。
require.ensure([ "./widgets/" + componentName ] ,(require) => {
let Component = require("./widgets/" + componentName);
});
但这似乎不起作用。
基本上它归结为预先创建您将需要的所有块。 然后你只需要一种动态引用它们的方法。 这是我基于我的解决方案:
http://henleyedition.com/implicit-code-splitting-with-react-router-and-webpack
这就是我所做的,因为我不使用 React Router(旁注:我认为它不适合 redux 或动画):
//loader:
{
test: (folder)\/.*\.js,
include: path.resolve(__dirname, 'src')
loader: ['lazy?bundle', 'babel']
}
//dynamic usage within React component:
const My_COMPONENTS = {
ComponentA: require('./folder/ComponentA'),
ComponentB: require('./folder/ComponentB'),
}
class ParentComponent extends React.Component {
componentDidMount() {
My_COMPONENTS[this.props.name](component => this.setState({component}));
}
render() {
return <this.state.component />;
}
}
所以结果是你正在动态渲染一个组件,但是来自一组静态的预先确定的可能性——同时,只发送给客户端的不是访问者实际感兴趣的块。
另外,这是我拥有的一个组件,它可以很好地做到这一点:
import React from 'react';
import Modal from './widgets/Modal';
export default class AjaxModal extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
Content: null
};
}
componentDidMount() {
if(this.props.show) {
this.loadContent();
}
}
componentWillReceiveProps({show}) {
if(show && !this.state.Content) {
this.loadContent(1200); //dont interfere with animation
}
}
loadContent(ms=0) {
setTimeout(() => {
this.props.requestLazyBundle(({default: Content}) => {
this.setState({Content});
});
}, ms);
}
render() {
let {Content} = this.state;
return (
<Modal title={this.props.title} {...this.props} loading={!Content}>
{Content ? <Content /> : null}
</Modal>
);
}
}
传递一个 async require bundler 函数作为this.props.requestLazybundle
,像这样:
render() {
let requestLazyBundle = require('bundle?lazy&name=AnotherComponent!../content/AnotherComponent');
return (
<AjaxModal title='Component Name' {...props} requestLazyBundle={requestLazyBundle} />
);
}
请查看我为完整源代码提供的这个要点页面https://gist.github.com/SamanShafigh/a0fbc2483e75dc4d6f82ca534a6174d4
因此,假设您有 4 个组件,分别称为 D1、D2、D3。 你需要的是创建一个依赖注入和一个依赖容器机制。 这是一个非常简单的实现
想象一下你有一个这样的配置文件来定义你的组件
export default [
{
name:'D1',
path:'D1'
},
{
name:'D2',
path:'D2'
},
{
name:'D3',
path:'D3'
}];
然后你可以有一个像这样的组件容器
import componentsConfig from 'ComponentsConfig';
let components = {};
for (var i = 0; i < componentsConfig.length; i++) {
let componentConfig = componentsConfig[i];
// Check if component is not already loaded then load it
if (components[componentConfig.name] === undefined) {
components[componentConfig.name] = require(`${componentConfig.path}`).default;
}
}
export default components;
最后,在您想要加载组件的地方,您可以使用组件容器动态加载组件,或者换句话说,您可以注入组件
import React, { Component } from 'react';
import ComponentContainer from './ComponentContainer';
class App extends Component {
render() {
let components = ['D1', 'D2', 'D3'];
return (
<div>
<h2>Dynamic Components Loading</h2>
{components.map((componentId) => {
let Component = ComponentContainer[componentId];
return <Component>{componentId}</Component>;
})}
</div>
);
}
}
export default App;
从 React 16.6.0 (2018 年 10 月 23 日) React.lazy()
,在客户端渲染代码拆分的旗帜下, React.lazy()
可能是一个方便的解决方案。
鉴于您希望延迟加载以下组件( MyLazy.js
):
import React from 'react';
export default function (props) {
return (
<p>My component</p>
);
}
您可以使用以下内容从另一个组件(例如App.js
)渲染它,注意内置<Suspense>
组件将显示直到加载完成:
import React, { Suspense } from 'react';
export function App(props) {
const MyLazyLoaded = React.lazy(() => import('./MyLazy'));
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<MyLazyLoaded />
</Suspense>
</div>
);
}
如果您想动态设置组件的名称,例如根据您的问题从用户输入,您可以使导入语句变量,例如:
var lazyComponentName = 'MyLazy';
const MyLazyLoaded = React.lazy(() => import('./' + lazyComponentName));
请注意,如果您不串联并直接使用变量名称(即import(lazyComponentName)
),您将看到以下 ES Lint 警告,与webpack相关:
关键依赖:依赖的请求是一个表达式
我正在寻找解决方案,然后我发现了 react-router npm 包。
https://www.npmjs.com/package/react-loadable
这是我的例子:
动态加载具有名称的组件。 首先获取加载项:
const MyComponent = Loadable({
loader: () => import(`../charts/${some_name}`),
loading: () => <div>Loading...</div>,
});
然后你有了它,你可以渲染它:
<MyComponent />
此阅读也可能有帮助:
https://itnext.io/react-loading-components-dynamically-a9d8549844c4
由于我的问题有点不同,我有一个不同的解决方案,我寻找了一段时间但找不到答案。 希望这可以帮助某人。
我的问题是我们有几个应用程序作为核心 UI 中的不同模块独立部署。 这些必须完全独立部署,才能对其他人产生任何影响。
我的解决方案是通过设置这些 webpack 属性将子组件应用程序包装为 webpack 库:
libraryTarget: 'umd',
globalObject: 'this',
library: 'SubComponentName'
我认为为应用程序提供了 2 个入口点,因为它可以直接访问,也可以通过单独应用程序中的父组件访问
class Library extends Component {
render() {
return (
< Provider store = {store} >< BrowserRouter>
< App isAccessDirect={this.props && this.props.isAccessDirect || false} roles={this.props.roles}>
</App>
< /BrowserRouter>< /Provider>
)
}
}
class Launcher extends Component {
render() {
return (
ReactDOM.render(
<Library isAccessDirect="true"/>,
document.getElementById('root')
)
)
}
}
构建此应用程序后,我可以直接在 index.html 中访问它
<script>
new Compression.Launcher().render();
</script>
或者,如果您希望按照我的要求通过单独的主机/组件访问应用程序,您可以使用 loadjs 和 react 的组合来实现。
loadjs(['../sub/component/bundle.min.js'], 'loadSubComponent');
var subComponent = this.getUI('subComponentWrapperElement').get(0);
loadjs.ready('loadSubComponent', function() {
var roles = my.app.roles;
ReactDOM.render(
<SubComponentName.Library roles={roles}></SubComponentName.Library>,
subComponent
);
});
这可以在任何类型的 javascript 客户端框架中加载到反应组件中,在我们的例子中,我们在包装器应用程序中使用了主干。 bundle.min.js 也位于反向代理后面,但它可以位于任何地方,只需加载它并等待加载完成,在这种情况下,loadjs 非常适合。 最后一个重要信息是,销毁组件对于确保良好的用户体验至关重要,因为我们遇到了许多问题。
loadjs.reset();
var subComponent = this.getUI('subComponentWrapperElement').get(0);
ReactDOM.unmountComponentAtNode(subComponent);
所以简而言之,我觉得这比其他答案提供的是一个完全独立的可部署应用程序套件,可以完全与框架无关。
import React, { Component } from "react";
class DynamicLoader extends Component {
render() {
const COMPONENTS = {
CompA: require('./CompA'),
CompB: require('./CompB'),
}
if(this.props.component && COMPONENTS[this.props.component]) {
let Component = COMPONENTS[this.props.component].default;
return <Component />
}
}
}
export default DynamicLoader;
<DynamicLoader component="CompA" />
这里有很多很好的答案。 我发现的另一种方法是将组件作为微应用程序延迟加载。 宿主应用程序甚至不需要在设计时安装或导入组件。
import React from 'react';
import ReactDOM from 'react-dom';
import MicroApp from '@schalltech/honeycomb-react-microapp';
const App = () => {
return (
<MicroApp
config={{
View: {
Name: 'redbox-demo',
Scope: 'beekeeper',
Version: 'latest'
}
}}
/>
);
});
export default App;
我按照下面的链接进行了实验。 这是一个非常有趣的概念。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.