繁体   English   中英

react组件的动态加载

[英]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;

我按照下面的链接进行了实验。 这是一个非常有趣的概念。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM