javascript - Webpack 成功构建,但 JavaScript 不执行运行时

标签 javascript reactjs webpack build compilation

我已将 Webpack v4 迁移到 v5,对 CLI 中的文档和弃用消息进行了相关更改,并成功构建,但在应用程序测试期间我注意到 JavaScript 无法运行并且没有错误。我得到了由 SSR 渲染的纯 html。

不知道为什么它不起作用,因为它很安静,可能是配置错误问题。

这是我的 webpack 配置:

const webpack = require('webpack');
const path = require('path');
const glob = require('glob');
// webpack plugins
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const AssetsPlugin = require('assets-webpack-plugin');
// minification plugins
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// image optimization plugins
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const imageminGifsicle = require("imagemin-gifsicle");
const imageminPngquant = require("imagemin-pngquant");
const imageminSvgo = require("imagemin-svgo");
const imageminMozjpeg = require('imagemin-mozjpeg');

const ProgressPlugin = webpack.ProgressPlugin;

const env = require('dotenv').config();

const isProd = process.env.NODE_ENV === 'production';
const isDev = !isProd;

const environment = {
    NODE_ENV: process.env.NODE_ENV || 'development',
    CONFIG: process.env.CONFIG || 'development',
    DEBUG: process.env.DEBUG || false,
};

const plugins = () => {
    const plugins = [
        new CleanWebpackPlugin({
            verbose: true,
            cleanStaleWebpackAssets: true,
        }),
        new webpack.EnvironmentPlugin(Object.assign(environment, env.parsed)),
        new MiniCssExtractPlugin({
            filename: '[contenthash:5].[name].css',
            chunkFilename: '[chunkhash:5].[name].css',
        }),
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            moment: 'moment',
            _: 'lodash',
        }),
        new webpack.ContextReplacementPlugin(/moment[\\\/]locale$/, /^\.\/(en)$/),
        new AssetsPlugin({
            filename: 'assets.json',
        }),
        new ImageminPlugin({
            cacheFolder: isDev ? path.resolve(__dirname, '../dist/cachedImages') : null,
            externalImages: {
                context: 'src',
                sources: glob.sync('src/assets/img/**/*.*'),
                destination: 'dist/img/',
                fileName: (filepath) => {
                    let name = filepath.split(/img(\/|\\)/).pop();
                    return name;
                },
            },
            plugins: [
                imageminGifsicle({
                    interlaced: false
                }),
                imageminMozjpeg({
                    progressive: true,
                    arithmetic: false
                }),
                imageminPngquant({
                    floyd: 0.5,
                    speed: 2
                }),
                imageminSvgo({
                    plugins: [
                        { removeTitle: true },
                        { convertPathData: false }
                    ]
                }),
            ],
        }),
    ];

    if (isProd) {
        plugins.push(
            new ProgressPlugin({
                entries: true,
                handler: (percentage, msg, ...args) => {
                    let moduleName = '';
                    if (args[2]) {
                        let sep = args[2].split(path.sep);
                        let dirIndex = sep.indexOf('finstead-client');
                        moduleName = sep.slice(dirIndex).join('/');
                    }

                    const currentWork = args[0] ? ` ${args[0]}` : '';
                    console.log(`[Progress${currentWork}]: (${(percentage * 100).toFixed(3)}%) ${msg} ${moduleName}`)
                }
            }),
            new BundleAnalyzerPlugin({
                analyzerMode: 'static',
                reportFilename: path.resolve(__dirname, 'analysis.html'),
                generateStatsFile: false,
                logLevel: 'info',
            })
        );
    }

    return plugins;
};

const optimization = () => {
    let optimizations = {
        moduleIds: isProd ? 'deterministic' : 'named',
        chunkIds: isProd ? 'deterministic' : 'named',
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/](react|react-dom|lodash|moment)[\\/]/,
                },
            }
        }
    };

    if (isProd) {
        optimizations.minimizer = [
            new TerserJSPlugin({
                terserOptions: {
                    compress: {
                        pure_funcs: ['console.log'],
                        drop_console: true,
                        drop_debugger: true
                    },
                    warnings: false
                },
                parallel: true
            }),
            new OptimizeCSSAssetsPlugin({})
        ];
    }

    return optimizations;
};

const fontLoaders = [
    {
        test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
        use: {
            loader: 'url?limit=10000&mimetype=application/font-woff',
        }
    },
    {
        test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
        use: {
            loader: 'url?limit=10000&mimetype=application/font-woff',
        }
    },
    {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        use: {
            loader: 'url?limit=10000&mimetype=application/octet-stream',
        }
    },
    {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        use: {
            loader: 'url?limit=10000&mimetype=image/svg+xml',
        }
    },
    {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        use: {
            loader: 'file-loader',
        }
    }
];

const config = {
    mode: process.env.NODE_ENV,
    devtool: isProd ?  false : 'eval-source-map',
    context: path.resolve(__dirname, '../src'),
    entry: {
        bundle: {
            import: './app.jsx',
            dependOn: 'vendor',
        },
        styles: './sass/main.scss',
        vendor: {
             runtime: 'runtimecode',
             import: [
            'react',
            'react-dom',
            'redux',
            'redux-saga',
            'react-redux',
            'react-router',
            'react-tap-event-plugin',
            'lodash',
            'moment',
             ]
        }
    },
    output: {
        publicPath: '/build/',
        filename: '[name].[contenthash].js',
        chunkFilename: '[name].[contenthash].js',
        path: path.resolve(__dirname, '../dist/build'),
    },
    resolve: {
        extensions: ['.js', '.jsx', '.json'],
        alias: {
            config: path.resolve(__dirname, '../config.js'),
            utils: path.resolve(__dirname, '../src/utils'),
            shared: path.resolve(__dirname, '../src/components/shared'),
            services: path.resolve(__dirname, '../src/services'),
            store: path.resolve(__dirname, '../src/store'),
            constants: path.resolve(__dirname, '../src/constants'),
            actions: path.resolve(__dirname, '../src/actions'),
            components: path.resolve(__dirname, '../src/components'),
        },
    },
    optimization: optimization(),
    plugins: plugins(),
    module: {
        rules: [
            {
                test: /\.m?jsx?$/,
                resolve: {
                    fullySpecified: false
                },
                // ignore transpiling JavaScript from node_modules as they should be ready to go OOTB
                exclude: /node_modules/,
            },
            {
                test: /\.jsx?$/,
                exclude: [/node_modules/, /libs/],
                use: {
                    loader: path.join(__dirname, '../helpers/custom-loader.js'),
                    options: {
                        presets: ['@babel/preset-env', '@babel/preset-react'],
                        plugins: [
                            '@babel/plugin-proposal-object-rest-spread',
                            '@babel/plugin-proposal-class-properties',
                            '@babel/plugin-transform-destructuring',
                            '@babel/plugin-syntax-dynamic-import',
                            '@babel/plugin-transform-runtime',
                            'syntax-async-functions'
                        ],
                        cacheDirectory: true
                    }
                }
            },
            {
                test: /\.scss$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            url: false,
                        }
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            implementation: require('node-sass'),
                        }
                    },
                ]
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.(jpe?g|png|gif|svg)$/i,
                loader: "url-loader",
                options: {
                    name: "[path][name].[ext]"
                },
            },
            ...fontLoaders
        ]
    },
    watchOptions: {
        ignored: /node_modules/,
    },
};

module.exports = config;

这是我使用的开发依赖项

"devDependencies": {
    "@babel/cli": "^7.12.10",
    "@babel/core": "^7.12.10",
    "@babel/node": "^7.12.10",
    "@babel/plugin-proposal-class-properties": "^7.12.1",
    "@babel/plugin-proposal-decorators": "^7.12.12",
    "@babel/plugin-proposal-export-namespace-from": "^7.12.1",
    "@babel/plugin-proposal-function-sent": "^7.12.1",
    "@babel/plugin-proposal-json-strings": "^7.12.1",
    "@babel/plugin-proposal-numeric-separator": "^7.12.7",
    "@babel/plugin-proposal-object-rest-spread": "^7.12.1",
    "@babel/plugin-proposal-throw-expressions": "^7.12.1",
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@babel/plugin-syntax-import-meta": "^7.10.4",
    "@babel/plugin-transform-destructuring": "^7.12.1",
    "@babel/plugin-transform-runtime": "^7.12.10",
    "@babel/preset-env": "^7.12.11",
    "@babel/preset-react": "^7.12.10",
    "@babel/register": "^7.12.10",
    "@babel/runtime": "^7.12.0",
    "@webpack-cli/migrate": "^1.1.2",
    "assets-webpack-plugin": "^7.0.0",
    "babel-eslint": "^10.0.3",
    "babel-loader": "^8.2.2",
    "babel-plugin-dynamic-import-webpack": "1.0.1",
    "babel-plugin-lodash": "^3.3.4",
    "babel-plugin-syntax-async-functions": "^6.13.0",
    "babel-plugin-system-import-transformer": "^3.1.0",
    "babel-plugin-transform-ensure-ignore": "^0.1.0",
    "babel-plugin-webpack-alias": "^2.1.2",
    "babel-watch": "^7.3.0",
    "clean-webpack-plugin": "^3.0.0",
    "cross-env": "^5.2.0",
    "css-loader": "^5.0.1",
    "eslint": "^6.8.0",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-import-resolver-alias": "^1.1.2",
    "eslint-plugin-import": "^2.22.0",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-react": "^7.22.0",
    "eslint-plugin-react-hooks": "^1.7.0",
    "file-loader": "^1.1.11",
    "imagemin-mozjpeg": "^9.0.0",
    "imagemin-pngquant": "^9.0.1",
    "imagemin-svgo": "^8.0.0",
    "imagemin-webpack-plugin": "^2.4.2",
    "imports-loader": "^0.7.0",
    "mini-css-extract-plugin": "^1.3.3",
    "nodemon": "^2.0.7",
    "optimize-css-assets-webpack-plugin": "^5.0.4",
    "redux-devtools": "^3.7.0",
    "redux-devtools-dock-monitor": "^1.2.0",
    "redux-devtools-log-monitor": "^1.2.0",
    "resolve-url-loader": "^2.3.2",
    "sass-loader": "^10.1.0",
    "terser-webpack-plugin": "^5.0.3",
    "webpack": "^5.11.1",
    "webpack-bundle-analyzer": "^4.3.0",
    "webpack-cli": "^4.3.1"
  },

编辑

如您所见,我正在起诉 assets-webpack-plugin,它为我提供了入口点和 block 的 JSON。我手动将 bundle.[contenthash].jsvendor.[contenthash].js 插入到成功加载的 index.html 中。

但是在编译的bundle的源代码中,我可以看到bundle不是运行时可执行函数,而是webpack模块。

我已经在 vendor 入口点尝试了 runtime 属性,因为主 bundle 点依赖于 vendor 包,即使它给了我可执行函数它不执行我的主捆绑代码。

最佳答案

经过更多调查,我发现,除了入口包和运行时文件之外,webpack 还创建了初始执行所需的几个文件。

因此,为了制作自定义 html 文件或使用任何其他预处理器文件,我们需要使用 html-webpack-plugin 基于可选提供的模板或 webpack-manifest-plugin 生成 html 文件 获取 json 文件中所需文件的列表。

...
plugins: [
    new WebpackManifestPlugin({
        fileName: path.resolve(__dirname, './manifest.json'), 
        filter: (file) => file.isInitial,
    }),
]
...

在这里,我们使用可用的过滤器选项来处理文件。 filter 函数提供 FileDescriptor 对象作为处理的输入,其中包含 isInitialisChunk 以及其他一些您可以使用的字段查找 documentation

正如您已经猜到的,标记为 isInitial 的文件是运行应用程序所需的。您还可以使用此插件生成自定义 list 文件。例如,我有一个文件,其中分离了初始 cssjs 包。

{
  "css": [
    "/build/c3a7e.commons.css"
  ],
  "js": [
    "/build/bundle.bf972748425cb97699f3.js",
    "/build/styles.09e2bc2ae43dc685a2c5.js",
    "/build/vendor.8e55b287c8b3ffc5c578.js",
    "/build/runtime.d9173e4531e516f18fc8.js",
    "/build/commons-app_jsx.1fc7ae7feba211c7a59d.js",
    "/build/vendor-node_modules_lodash_lodash_js-node_modules_moment_moment_js-node_modules_react-dom_index_js.fc6e3899340455918b6a.js",
    "/build/commons-node_modules_moment_locale_sync_recursive_en_-node_modules_react-redux_es_index_js-no-b2e9a0.a38dfe96c879814994af.js"
  ]
}

关于javascript - Webpack 成功构建,但 JavaScript 不执行运行时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65625040/

相关文章:

javascript - 需要帮助使用 jquery 计时事件

javascript - 如何修复在 componentWillMount 中设置状态时的日志记录问题

javascript - 强制 SWFObject 使用 EMBED 而不是 OBJECT

javascript - Google Charts API 自定义月份名称

javascript - 变量在 fetch 中更改但在函数外部未更改

使用 Webpack 编译的 Javascript 失败并注入(inject)了奇怪的代码

Webpack:在缺少成员导入时抛出错误

javascript - 将 Mediator 模式与 webpack 和 ES6 模块导入导出结合使用

javascript - 点击功能中的 sencha touch HTMLDivElement

javascript - 控制条件警报,并在 ReactJS 中滚动到它