动态加载反应组分

时间:2016-04-30 08:05:58

标签: reactjs webpack

我需要动态加载react组件。

我得到要从用户加载为字符串的组件的名称。我使用 webpack

如何动态加载组件而不是静态导入语句。似乎Require.Ensure没有评估表达式。我想要实现的是这样的。

require.ensure([ "./widgets/" + componentName ] ,(require) => {
    let Component = require("./widgets/" + componentName);   
});

但这似乎不起作用。

7 个答案:

答案 0 :(得分:16)

基本上它归结为预先创建你需要的所有块。那么你只需要一种动态引用它们的方法。以下是我所依据的解决方案:

http://henleyedition.com/implicit-code-splitting-with-react-router-and-webpack

这就是我所做的事情,因为我不使用React Router(旁注:我发现它不适合还原或动画):

//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} />
  );
}

答案 1 :(得分:6)

请查看我为完整源代码提供的这个主页 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;

答案 2 :(得分:1)

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" />

答案 3 :(得分:1)

截至React 16.6.0(2018年10月23日),React.lazy()可能是一种方便的解决方案,在code-splitting的旗帜下进行了客户端渲染。

固定的组件名称

给出您希望延迟加载的以下组件(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>
  );

}

动态组件名称

如果要动态设置组件的名称,例如根据用户输入的问题,可以将import语句设置为变量,例如:

var lazyComponentName = 'MyLazy';
const MyLazyLoaded = React.lazy(() => import('./' + lazyComponentName));

请注意,如果您没有串联并直接使用变量名(即import(lazyComponentName)),则会看到与webpack相关的以下ES Lint警告:

关键依赖项:依赖项的请求是一个表达式

答案 4 :(得分:0)

我的问题有所不同,因此我有一个不同的解决方案,我寻找了一段时间的答案,但找不到答案。希望这可以对某人有所帮助。

我的问题是我们在核心UI中将多个应用程序作为不同的模块独立部署。这些必须完全独立部署,以对其他任何方面都没有影响。

我的解决方案是通过设置以下Webpack属性,将子组件应用程序包装为Web Pack库:

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客户端框架中的react组件中,在本例中,我们在包装应用程序中使用了主干。 bundle.min.js也位于反向代理后面,但它可以位于任何地方,仅需将其加载并等待加载完成,在这种情况下,loadjs非常适合。最后一点重要信息是,销毁组件对于确保良好的用户体验至关重要,因为我们没有遇到很多问题。

loadjs.reset();
var subComponent = this.getUI('subComponentWrapperElement').get(0);
ReactDOM.unmountComponentAtNode(subComponent);

因此,总的来说,我觉得这提供的是其他完全独立于框架的应用程序套件,这些套件可以与框架完全无关。

答案 5 :(得分:0)

这里有很多好的答案。我发现的另一种方法是将组件延迟加载为微型应用程序。宿主应用程序甚至不需要在设计时安装或导入组件。

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;

我通过以下链接进行了实验。这是一个非常有趣的概念。

https://github.com/Schalltech/honeycomb-marketplace

答案 6 :(得分:0)

我正在寻找解决方案,然后我发现了 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