Webpack 5模块联合-远程模块中的挂钩-不起作用

问题描述

我正在尝试借助Module Federation(webpack 5功能)在运行时获得动态系统。一切正常,但是当我将钩子添加到“生产者”模块(主机应用程序从中动态导入组件的模块)时,会出现大量“钩子无效规则”错误

Warning: Do not call Hooks inside useEffect(...),useMemo(...),or other built-in Hooks. You can only call Hooks at the top level of your React function. For more information,see [LINK RULES OF HOOKS]
Warning: React has detected a change in the order of Hooks called by PluginHolder. This will lead to bugs and errors if not fixed. For more information,read the Rules of Hooks: [LINK RULES OF HOOKS]

我已经使用了externals字段并在html文件添加了脚本标签,我使用了共享选项并添加了singleton字段:true并指定react和react-dom版本 每次控制台吐出大量错误

这是我直接从文档中加载模块的方法

const loadComponent = (scope: string,module: string) => async (): Promise<any> => {
    // @ts-ignore
    await __webpack_init_sharing__('default');
    // @ts-ignore
    const container = window[scope];
    // @ts-ignore
    await container.init(__webpack_share_scopes__.default);
    // @ts-ignore
    const factory = await window[scope].get(module);
    return factory();
};

要加载remoteEntry.js文件,我将makeAsyncScriptLoader HOC与react-async-script一起使用,如下所示:

const withScript = (name: string,url: string) => {
    const LoadingElement = () => {
        return <div>Loading...</div>;
    };

    return () => {
        const [scriptLoaded,setScriptLoaded] = useState<boolean>(false);

        const AsyncScriptLoader = makeAsyncScriptLoader(url,{
            globalName: name,})(LoadingElement);

        if (scriptLoaded) {
            return <PluginHolder name={name}/>;
        }

        return (
            <AsyncScriptLoader
                asyncScriptOnLoad={() => {
                    setScriptLoaded(true);
                }}
            />
        );
    };
};

PluginHolder是一个简单的组件,它包装了已加载脚本中的加载模块(加载实际上已完成)

 useEffect((): void => {
        (async () => {
            const c = await loadComponent(name,'./Plugin')();
            setComponent(c.default);
        })();
    },[]);

 return cloneElement(component);

最重要的是入门程序:

const [plugins,setPlugins] = useState<PluginFunc[]>([]);

    useEffect((): void => {
        pluginNames.forEach(desc => {
            const loaded = withScript(desc.name,desc.url);
            setPlugins([...plugins,loaded]);
        });
    },[]);

我不使用React.Lazy,因为我不能使用import()。此外,在主机应用程序中,我设置了渴望的字段:react和react-dom

为true

下面的我的webpack.config.js(主机):

require('tslib');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { DefinePlugin } = require('webpack');
const { ModuleFederationPlugin } = require('webpack').container;
// @ts-ignore
const AutomaticvendorFederation = require('@module-federation/automatic-vendor-federation');
const packageJson = require('./package.json');
const exclude = ['babel','plugin','preset','webpack','loader','serve'];
const ignoreversion = ['react','react-dom'];

const automaticvendorFederation = AutomaticvendorFederation({
    exclude,ignoreversion,packageJson,shareFrom: ['dependencies','peerDependencies'],ignorePatchVersion: false,});

module.exports = {
    mode: 'none',entry: {
        app: path.join(__dirname,'src','index.tsx'),},target: 'web',resolve: {
        extensions: ['.ts','.tsx','.js'],module: {
        rules: [
            {
                test: /\.tsx?$/,exclude: '/node_modules/',use: 'ts-loader',{
                test: /\.(s[ac]|c)ss$/i,use: [
                    'style-loader','css-loader','sass-loader',],plugins: [
        new HtmlWebpackPlugin({
            template: path.join(__dirname,'public','index.html'),favicon: path.join(__dirname,'favicon.ico'),}),new DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('development'),new ModuleFederationPlugin({
            name: 'host',remotes: {},exposes: {},shared: {
                ...automaticvendorFederation,react: {
                    eager: true,singleton: true,requiredVersion: packageJson.dependencies.react,'react-dom': {
                    eager: true,requiredVersion: packageJson.dependencies['react-dom'],output: {
        filename: '[name].js',path: path.resolve(__dirname,'dist'),publicPath: 'http://localhost:3001/',devServer: {
        contentBase: path.join(__dirname,port: 3001,};

还有第二个模块中的webpack.config.js:

require('tslib');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { DefinePlugin } = require('webpack');
const { ModuleFederationPlugin } = require('webpack').container;
// @ts-ignore
const AutomaticvendorFederation = require('@module-federation/automatic-vendor-federation');
const packageJson = require('./package.json');
const exclude = ['babel',});

module.exports = (env,argv) => {

    const { mode } = argv;
    const isDev = mode !== 'production';

    return {
        mode,entry: {
            plugin: path.join(__dirname,resolve: {
            extensions: ['.ts',module: {
            rules: [
                {
                    test: /\.tsx?$/,{
                    test: /\.(s[ac]|c)ss$/i,use: [
                        'style-loader',plugins: [
            new HtmlWebpackPlugin({
                template: path.join(__dirname,new DefinePlugin({
                'process.env.NODE_ENV': JSON.stringify('development'),new ModuleFederationPlugin({
                name: 'example',library: { type: 'var',name: 'example' },filename: 'remoteEntry.js',exposes: {
                    './Plugin': './src/Plugin',shared: {
                    ...automaticvendorFederation,react: {
                        eager: isDev,'react-dom': {
                        eager: isDev,output: {
            path: path.resolve(__dirname,publicPath: 'http://localhost:3002/',devServer: {
            contentBase: path.join(__dirname,port: 3002,};
};

您是否有任何经验或任何线索-我想关键是2个应用程序使用2个react实例,但这是我的猜测。 我的配置有问题吗?

解决方法

确保将共享依赖项添加到 webpack.config 文件中。

请参见以下示例:

  plugins: [
    new ModuleFederationPlugin(
      {
        name: 'MFE1',filename:
          'remoteEntry.js',exposes: {
          './Button':'./src/Button',},shared: { react: { singleton: true },"react-dom": { singleton: true } },}
    ),new HtmlWebpackPlugin({
      template:
        './public/index.html',}),],};

我使用此共享属性设置了主机和远程项目。当钩子破坏了我的主机应用程序时为我修复了它。因为有重复的react依赖,不管版本是否一样都会报错。​​