You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Webpack Dev Server运行完成后自动删除Bundle文件问题求助

问题:React库中webpack-dev-server热更新时Bundle文件被删除

我正尝试在React库中使用启用热更新(HMR)的webpack-dev-server,核心目标是实现模块热替换。但每次serve运行完成后,Bundle文件似乎都会被删除。

命令行输出

$ clear && npx webpack serve --mode=development

<i> [webpack-dev-server] Project is running at: "/tmp/webpack-dev-server.sock"
<i> [webpack-dev-server] Content not from webpack is served from '/usr/local/src/foo/View/public' directory
asset bundle_ff16b3001589ab53ae37.js 6.39 MiB [emitted] [immutable] (name: main)
orphan modules 1010 KiB [orphan] 233 modules
runtime modules 28.5 KiB 14 modules
cacheable modules 5.65 MiB
  modules by path ./node_modules/ 5.26 MiB 219 modules
  modules by path ./src/ 394 KiB
    modules by path ./src/components/ 389 KiB 90 modules
    modules by path ./src/lib/ 2.42 KiB
      modules by path ./src/lib/util/*.ts 778 bytes 2 modules
      + 2 modules
    modules by path ./src/*.js 2.72 KiB
      ./src/export.js 1.06 KiB [built] [code generated]
      ./src/constants.js 808 bytes [built] [code generated]
      ./src/helpers.js 899 bytes [built] [code generated]
external "React" 42 bytes [built] [code generated]
external "ReactDOM" 42 bytes [built] [code generated]
webpack 5.90.3 compiled successfully in 8966 ms

...

$ ll public
total 0

webpack.config.js

const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

/**
 * Path resolver - automatically resolve alias from @folder into ./src/folder
 * @type {string}
 */
const srcDir = path.resolve(__dirname, 'src');
const aliases = fs.readdirSync(srcDir)
    .filter((item) => fs.statSync(path.join(srcDir, item)).isDirectory())
    .reduce((acc, item) => {
        acc[`@${item}`] = path.join(srcDir, item);
        return acc;
    }, {});

let config = {
    entry: path.join(__dirname, 'src/export.js'),
    watchOptions: {
        aggregateTimeout: 200,
        poll: 1000,
    },
    module: {
        rules: [
            {
                test: [/\.js$/, /\.jsx$/],
                exclude: [/node_modules/],
                loader: 'babel-loader'
            },
            {
                test: [/\.tsx?$/, /\.ts?$/],
                exclude: [/node_modules/],
                use: 'ts-loader'
            },
            {
                test: /\.css$/,
                exclude: [/node_modules/],
                loader: 'css-loader'
            },
            {
                test: /\.scss$/,
                exclude: [/node_modules/],
                use: ['style-loader', 'css-loader', 'sass-loader']
            },
            {
                test: /\.(png|jpe?g|gif)$/i,
                exclude: [/node_modules/],
                type: 'asset/resource'
            },
            {
                test: /\.svg$/,
                type: 'asset',
                use: ['@svgr/webpack'],
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/i,
                type: 'asset/inline',
                loader: 'url-loader',
            }
        ]
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({
            options: {
                globalObject: '(typeof self !== \'undefined\' ? self : this)'
            }
        }),
        new webpack.ProvidePlugin({
            process: 'process/browser',
        }),
    ],
    resolve: {
        fallback: {
            'process/browser': require.resolve('process/browser'),
        },
        alias: {
            ...aliases,
            '@scss': path.resolve(__dirname, 'src/static/scss'),
        },
        extensions: ['', '.js', '.jsx', '.ts', '.tsx']
    },
    output: {
        path: path.join(__dirname, '/public'),
        library: {
            name: 'foo',
            type: 'umd'
        },
        filename: 'bundle_[contenthash].js',
        clean: true
    },
    externals: {
        'react': 'React',
        'react-dom': 'ReactDOM'
    },
};

module.exports = (env, argv) => {
    if (argv.mode === 'production') {
        config.optimization = {
            ...config.optimization,
            minimizer: [new TerserPlugin({
                extractComments: false,
            })],
            minimize: true,
            plugins: [
                ...config.plugins,
                new CleanWebpackPlugin()
            ]
        };
    } else {
        config = {
            ...config,
            // development by default
            mode: 'development',
            devServer: {
                hot: true,
                ipc: true,
                watchFiles: ['./src/**/**'],
            },
        };
    }

    return config;
};

解决方案

核心原因

  1. webpack-dev-server内存存储机制:开发模式下,webpack-dev-server默认将编译后的bundle文件存在内存中,不会写入public目录,所以磁盘上看不到文件是正常的,服务仍能访问内存中的资源。
  2. output.clean的全局生效:你在全局output配置中设置了clean: true,该选项会在编译前清空public目录。即使dev-server不写磁盘,这个清理动作依然会执行,导致目录最终为空。

具体修复步骤

1. 仅在生产模式启用目录清理

把output.clean移到生产模式分支,避免开发阶段清空目录:

// 全局config的output中移除clean: true
output: {
    path: path.join(__dirname, '/public'),
    library: {
        name: 'foo',
        type: 'umd'
    },
    filename: 'bundle_[contenthash].js'
    // 删除 clean: true
},

// 在production分支中添加clean配置
if (argv.mode === 'production') {
    config.output = {
        ...config.output,
        clean: true
    };
    // 其余生产模式配置保持不变
}

2. (可选)让dev-server写入磁盘

如果需要在开发阶段查看磁盘上的bundle文件,可在devServer配置中添加writeToDisk: true:

devServer: {
    hot: true,
    ipc: true,
    watchFiles: ['./src/**/**'],
    writeToDisk: true // 添加该行
},

注意:开启后会略微降低编译速度,因为需要额外执行磁盘写入操作。

3. 完善React HMR配置(确保热更新生效)

当前仅开启了hot: true,React模块热替换需要额外配置:

  • 安装依赖:
npm install -D react-refresh @pmmmwh/react-refresh-webpack-plugin
  • 修改webpack配置:
// 顶部引入插件
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

// 在development分支中更新配置
else {
    config = {
        ...config,
        mode: 'development',
        devServer: {
            hot: true,
            ipc: true,
            watchFiles: ['./src/**/**'],
        },
        plugins: [
            ...config.plugins,
            new ReactRefreshWebpackPlugin() // 添加热更新插件
        ],
        module: {
            ...config.module,
            rules: config.module.rules.map(rule => {
                // 给babel-loader添加react-refresh预设
                if (rule.loader === 'babel-loader') {
                    return {
                        ...rule,
                        options: {
                            presets: ['@babel/preset-react'],
                            plugins: ['react-refresh/babel']
                        }
                    };
                }
                return rule;
            })
        }
    };
}

同时确保你的babel配置文件(.babelrc或babel.config.js)包含@babel/preset-react预设。

内容的提问来源于stack exchange,提问作者fiskolin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 11:53:16