React.Lazy在使用Webpack 5和插件Module Federation的Micro前端时不工作

jhiyze9q  于 2023-08-06  发布在  Webpack
关注(0)|答案(1)|浏览(161)

发生的事情是,我正在通过模块联合使用Micro前端,但即使使用React.Lazy,bundle(remoteEntry)也会在应用程序开始时加载,而不会访问导入组件的路由。

要模拟场景,您可以访问存储库并按照所述步骤操作。click here to access.

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const dependencies = require('./package.json').dependencies;
const ModuleFederationPlugin =
  require('webpack').container.ModuleFederationPlugin;
const path = require('path');
const dotenv = require('dotenv');

module.exports = (_, args) => {
  return {
    mode: args.mode,
    entry: './src/index.js',
    output: {
      filename:
        process.env.NODE_ENV === 'development'
          ? 'main.js'
          : 'main.[contenthash].js',
      publicPath: process.env.NODE_ENV === 'development' ? '/' : '/app/',
      path: path.resolve(__dirname, 'build')
    },
    devServer: {
      static: {
        directory: path.join(__dirname, 'build')
      },
      port: 3001,
      historyApiFallback: true
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.js', '.jsx'],
      alias: {
        actions: path.resolve(__dirname, 'src', 'actions'),
        components: path.resolve(__dirname, 'src', 'components'),
        containers: path.resolve(__dirname, 'src', 'containers'),
        'custom-hooks': path.resolve(__dirname, 'src', 'custom-hooks'),
        enums: path.resolve(__dirname, 'src', 'enums'),
        helpers: path.resolve(__dirname, 'src', 'helpers'),
        hooks: path.resolve(__dirname, 'src', 'hooks'),
        images: path.resolve(__dirname, 'src', 'images'),
        libs: path.resolve(__dirname, 'src', 'libs'),
        middlewares: path.resolve(__dirname, 'src', 'middlewares'),
        reducers: path.resolve(__dirname, 'src', 'reducers'),
        sagas: path.resolve(__dirname, 'src', 'sagas'),
        services: path.resolve(__dirname, 'src', 'services'),
        store: path.resolve(__dirname, 'src', 'store'),
        views: path.join(__dirname, 'src', 'views'),
        routes: path.join(__dirname, 'src', 'routes')
      },
      fallback: {
        fs: false,
        tls: false,
        net: false,
        path: false,
        zlib: false,
        http: false,
        https: false,
        stream: false,
        crypto: false,
        'styled-components': require.resolve('styled-components'),
        util: false
      }
    },
    module: {
      rules: [
        {
          test: /\.jsx?$/,
          loader: 'babel-loader',
          exclude: /node_modules/,
          options: {
            presets: ['@babel/preset-react']
          }
        },
        {
          test: /\.css$/i,
          use: ['style-loader', 'css-loader']
        },
        {
          test: /\.s[ac]ss$/i,
          use: ['style-loader', 'css-loader', 'sass-loader']
        },
        {
          test: /\.(png|jpg|jpeg|gif|pdf)$/,
          exclude: /node_modules/,
          use: ['file-loader?name=[name].[ext]']
        },
        {
          test: /\.svg$/,
          use: ['@svgr/webpack', 'url-loader']
        }
      ]
    },

    plugins: [
      new webpack.ProgressPlugin(),
      new CleanWebpackPlugin(),
      new CopyPlugin({
        patterns: [{ from: 'public/config.json', to: 'config.json' }]
      }),
      new ModuleFederationPlugin({
        name: 'connect_front',
        remotes: {
          connect_vim_front: `promise new Promise((resolve, reject) => fetch('${process.env.PUBLIC_URL}config.json', {
            method: 'GET'
          })
            .then(resp => {
                            return resp.text();
                        }).then((resp) => {
                            const parsedResp = JSON.parse(resp);
                            const script = document.createElement('script');
                            script.src = parsedResp.REACT_APP_URL_CONNECT_VIM_FRONT || "/",
                            script.onload = () => {
                                const proxy = {
                                    get: (request) => window.connect_vim_front.get(request),
                                    init: (arg) => {
                                        try {
                                            return window.connect_vim_front.init(arg)
                                        } catch(e) {
                                            console.error('remote container already initialized')
                                        }
                                    }
                                }
                                resolve(proxy)
                            }

                            script.onerror = function() {
                                reject();
                            };

                            document.head.appendChild(script)
                        })
            .catch(err => console.log(err)))`
        },
        shared: {
          ...dependencies,
          react: {
            singleton: true,
            eager: true,
            requiredVersion: dependencies.react
          },
          'react-dom': {
            singleton: true,
            eager: true,
            requiredVersion: dependencies['react-dom']
          }
        }
      }),
      new HtmlWebpackPlugin({
        template: './public/index.html',
        filename: './index.html',
        favicon: './public/favicon.ico',
        title: 'Caching'
      }),
      new webpack.ProvidePlugin({
        process: 'process/browser'
      }),
      new webpack.DefinePlugin({
        'process.env': JSON.stringify(
          dotenv.config({
            path: `${
              process.env.ENVIRONMENT
                ? `.env.${process.env.ENVIRONMENT}`
                : '.env'
            }`
          }).parsed
        )
      })
    ]
  };
};

字符串
路线

import React, { Suspense } from 'react';

import Route from './Route';

const Vim = React.lazy(() => import('views/vim'));

export const AuthenticatedRoutes = ({ skipRedirect }) => {
  return (
    <>
      <Route
        exact
        path='/...'
        component={component}
      />
      <Route
        skipRedirect={skipRedirect}
        isPrivate
        exact
        path='/vim'
        component={
          <Suspense fallback={<div>Loading...</div>}>
            <Vim />
          </Suspense>
        }
      />
      <Route
        exact
        path='/...'
        component={component}
      />
    </>
  );
};


页面组件

import React from 'react';
import ModuleLoader from '../../components/ModuleLoader';
import { FallbackLoading } from '../../components/FallbackLoading';

import { VimPageWrapper } from './VimPageWrapper';

const Vim = () => {
  return (
    <ModuleLoader fallback={<FallbackLoading />}>
      <VimPageWrapper />
    </ModuleLoader>
  );
};

export default Vim;


消费MFE

import React from 'react';
import ModuleLoader from 'components/ModuleLoader';
import { FallbackLoading } from 'components/FallbackLoading';
const VimPage = React.lazy(() => import('connect_vim_front/vim-page'));
const Wrapper = React.lazy(() => import('connect_vim_front/Wrapper'));

export const VimPageWrapper = () => {
  return (
    <ModuleLoader fallback={<FallbackLoading />}>
      <Wrapper>
        <VimPage />
      </Wrapper>
    </ModuleLoader>
  );
};


捆绑包(remoteEntry)加载即使没有访问路由在这里插入图像的描述

ModuleLoader

import React, { Suspense } from 'react';
import { FallbackError } from '../FallbackError';

class ModuleLoader extends React.Component {
  constructor(props) {
    super(props);

    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    const { fallback, children } = this.props;
    const { hasError } = this.state;

    if (hasError) {
      return <FallbackError />;
    }

    return <Suspense fallback={fallback}>{children}</Suspense>;
  }
}

export default ModuleLoader;

hgqdbh6s

hgqdbh6s1#

您可以使用此软件包module-federation-import-remote

import { importRemote } from "module-federation-import-remote";

  // If it's a regular js module:
  importRemote({ url: "http://localhost:3001", scope: 'Foo', module: 'Bar' }).then(({/* list of Bar exports */}) => {
    // Use Bar exports
  });

  // If Bar is a React component you can use it with lazy and Suspense just like a dynamic import:
  const Bar = lazy(() => importRemote({ url: "http://localhost:3001", scope: 'Foo', module: 'Bar' }));

  return (
    <Suspense fallback={<div>Loading Bar...</div>}>
      <Bar />
    </Suspense>
  );

字符串
将Vue组件导入到React应用程序中,例如:
而不是在webpack.config.js中配置它

remotes: {
     DashboardApp: "DashboardApp@http://localhost:9005/remoteEntry.js",
},


创建这样的组件

export default (props) => {
    const ref = useRef(null);
    
    useEffect(() => {
        importRemote({ url: "http://localhost:9005", scope: 'DashboardApp', module: 'DashboardPage' })
            .then(({mount}) => {
                mount(ref.current, props)
        });
    }, [])

    return <div ref={ref} />
}

相关问题