【问题标题】:Delete unused webpack chunked files删除未使用的 webpack 分块文件
【发布时间】:2015-07-30 23:10:29
【问题描述】:

我正在寻找有关如何删除旧的 webpack 分块文件的信息。这是我当前的 webpack 配置:

var path = require('path');
var webpack = require('webpack');

module.exports = {
  debug: false,
  outputPathinfo: true,
  displayErrorDetails: true,
  context: __dirname,
  entry: {
    common: ['./src/common.coffee'],
    a: './src/a.cjsx',
    b: './src/b.cjsx'
  },
  output: {
    filename: '[name]-[chunkhash].js',
    chunkFileName: '[name].[chunkhash].js',
    path: path.join(__dirname, 'js')
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin('common', 'common-[chunkhash].js'),
    new webpack.optimize.UglifyJsPlugin({
      compress: { warnings: false }
    })
  ],
  module: {
    preLoaders: [
      {
        test: /\.coffee$/,
        exclude: /node_modules/,
        loader: 'coffeelint-loader'
      }
    ],
    loaders: [
      { test: /\.coffee/, loader: 'coffee' },
      { test: /\.cjsx$/, loaders: ['coffee', 'cjsx'] },
      { test: /\.js$/, loader: 'jsx-loader?harmony' }
    ]
  }
}

如果我正在运行 $(npm bin)/webpack --config webpack.js --watch 并对 a.cjsx 进行更改,它会使用新的 chunkedhash 编译该文件的更新版本。但是,旧的仍然存在,我希望立即将其删除。

  1. 如何删除旧版本的分块文件?
  2. 有没有办法让我在手表完成编译后挂钩到后回调?

【问题讨论】:

    标签: javascript webpack


    【解决方案1】:

    有一个clean-webpack-plugin 用于这些目的,或者您可以为npm 编写一个简单的bash 脚本:

     "scripts": {
        "build": "rm -r dist/* && webpack -p",
        "clean": "rm -r dist/*"
      }
    

    【讨论】:

    • 作者的意思是使用--watch cli选项时如何删除旧的chunkhash
    • 不幸的是,clean-webpack-plugin 确实适用于webpack --watch(它会清理一次,之后每次更改时同一文件的多个版本都会开始堆积);可能是因为它并不真正知道哪些块已过时。
    • 感谢您的 B 计划 - 为我节省了数小时试图不让 clean-webpack-plugin 删除内容两次的沮丧。
    • @klewis 你可以使用 'webpack-clean-obsolete-chunks' 删除未使用的块文件
    【解决方案2】:

    这是webpack-clean-obsolete-chunks 插件,它可以满足您的需求。它会在每次 webpack 编译后搜索所有更新的块并删除过时的文件。

    【讨论】:

    • 由于某些原因它什么也没做
    • 仅供watch使用
    【解决方案3】:

    答案

    我决定写一个答案,因为其他人 - 尽管试图直接回答这个问题 - 忽略了我认为最重要的部分。

    最重要的是:你不应该这样做。在你的开发设置中使用[hash] 占位符会给其他工具带来很多麻烦(例如 symfony 插件中 phpstorm 的路径自动完成)。此外,它对于 webpack 的增量编译性能也很差,因此官方 webpack 文档 (reference) 不推荐它。

    所以对于未来的读者:只需保持简单的开发配置 - 将您的 filename 定义为 [name].js 并继续。

    编辑

    对于如何处理生产服务器上的旧块文件似乎存在混淆。好吧,你什么都不做。一旦部署了一个版本,就不应再更改它。您只需在部署时不断创建新版本并将以前的版本作为备份。为什么?

    因为您希望您的回滚是可靠的,并且您的回滚需要非常简单和原子化。如果您的回滚过程不仅仅是切换符号链接、重新路由到先前的容器(或类似的简单操作),那么您可能会遇到麻烦。

    回滚不是再次“重新部署”应用程序的过程,而是现在回滚到以前的版本。这是一个“取消”部署的过程。因此,当您的生产应用程序挂在那里时,对以前的版本执行 git checkout 后跟 npm build --but-please-be-hurry --and-im-begging-you-dont-fail,完全爆炸不会在这里剪切。

    重建以前版本的应用程序 - 就像部署一样 - 可能由于多种原因而失败。这就是为什么回滚应该切换/重新路由回被证明有效的完全相同的版本构建。不是==-the-same,100% ===-the-same。这就是为什么您需要保留以前的版本,因为那是===-same。 “重新生成”的是 - 在最好的情况下 - 只有 ==-the-same,所以它没有被证明是有效的,只是假设。

    不,再多的 CI、暂存环境或任何东西都不能保证您的部署成功。以正确的方式做事的一部分是为出现问题做好准备。事情出错。希望只是偶尔,但仍然如此。

    当然,一旦您备份了 3、5 或 <put-your-number-here> 版本,您可能会开始删除最旧的版本,因为您可能永远不需要超过 3 个。

    【讨论】:

    • 正确答案。我认为有些人可能只是对 [hash] 感到困惑,并且没有意识到您可以有条件地使用它们,具体取决于您是处于开发阶段还是生产阶段。
    • 以及如何清理生产版本中的块文件?所有旧的块文件都保留下来。
    • 好吧,prod 中的问题似乎比 dev 中更大。在本地,我可以不时地删除文件夹而没有风险。在服务器上,这可能会在生成新包时导致停机和意外行为等
    • @mousetail in prod 完全不用担心 - 帮自己一个忙,并设置一个自动部署管道,其中新版本在后台构建(在新目录中,在一个新的 docker 容器或其他任何东西中),一旦完成, trffic 就会重新路由到最新版本(通过切换符号链接或在负载均衡器上重定向,或其他)。没有停机时间,也没有旧文件,因为旧版本会在一段时间后被销毁。为了简单起见,我会推荐符号链接方法,但这是一个偏好和要求的问题。
    • @mousetail 使用符号链接方法,您可以轻松地手动完成,但它变得乏味且容易出错。这就是我推荐自动化解决方案的原因,而且 github 上有很多这样的部署工具包。
    【解决方案4】:

    从 Webpack 5.20.0 开始,您可以使用 output.clean 选项

    【讨论】:

      【解决方案5】:

      看看这个拉取请求: https://github.com/johnagan/clean-webpack-plugin/pull/32/files

      打开原始文件视图并将其复制到干净的 webpack 插件的 index.js 中。 记住配置标志 -> watch: true

      【讨论】:

        【解决方案6】:

        我已经通过在webpack.config.js 中添加以下内容解决了这个问题

        const { CleanWebpackPlugin } = require('clean-webpack-plugin');    
        {
          ... your configs ...
          plugins: [new CleanWebpackPlugin()]
        }
        

        【讨论】:

        【解决方案7】:

        看起来 webpack@5.20.0+ 已经内置了对这个https://webpack.js.org/configuration/output/#outputclean 的支持。我在我的块文件名中使用[chunkhash],如果我停止注释掉动态导入,它们会被清除,如果我取消注释它们,它们会被重新添加。

        【讨论】:

          【解决方案8】:

          我的案例:webpack 5 + 多页应用程序 + Themes.css 通过入口点

          解决方案:https://github.com/webdiscus/webpack-remove-empty-scripts

          此插件不适用于 webpack 5 入口点或 MiniCssExtractPlugin:

          webpack-fix-style-only-entries, webpack-extraneous-file-cleanup-plugin, webpack-remove-empty-js-chunks-plugin, webpack-delete-no-js-entries-plugin.

          我的webpack.config.js:

          const fs = require('fs');
          const path = require('path');
          const MiniCssExtractPlugin = require('mini-css-extract-plugin');
          const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
          
          const isProd = process.env.NODE_ENV === 'production';
          const isDev = !isProd;
          
          const PAGES = ['app', 'help'];
          
          const getThemes = (themePath, alias) => {
            let themes = {};
            const longPath = './' + alias + '/' + themePath;
            fs.readdirSync(longPath).forEach(function(fileName) {
              const fileNameWithPath = path.join(themePath, fileName);
              const fileNameWithLongPath = path.join(longPath, fileName);
          
              const stat = fs.lstatSync(fileNameWithLongPath);
              if (stat.isDirectory()) return;
              if (!/\.scss$/.test(fileName)) return;
          
              const nameWithoutExt = path.basename(fileName, '.scss');
              themes[nameWithoutExt] = ['./' + fileNameWithPath];
            });
            console.log(themes);
            return themes;
          };
          
          const themes = getThemes('scss/themes', 'src');
          
          const getFilename = (filename, ext) => {
            let name = filename == 'index' ? 'bundle' : filename;
            const isTheme = (ext == 'css' && name.startsWith('theme')) ? true : false;
            const needHash = (isDev || isTheme) ? false : true;
            return needHash ? name +`.[fullhash].` + ext : name+'.'+ext;
          };
          
          const getCSSDirname = filename => {
            const isTheme = filename.startsWith('theme');
            return !isTheme? '/css/' : '/css/theme/';
          };
          
          const getHTMLWebpackPlugins = arr => {
            // this function config multipages names and add to html-pages
            // inside <head> tag our themes via tag <link rel="stylesheet" href="....css" ...>
            // and return array of HTMLWebpackPlugins
          };
          
          module.exports = {
          // ... //
            entry: {
              // mutipage:
              app:  ['./index.js', './scss/app.scss'], 
              help: ['./help.js', './scss/help.scss'],
              // multitheme:
              ...themes,
            },
            optimization: {
              removeEmptyChunks: true, // not work!!!
            },
            // ... //
            plugins: [
              // ... //
              ...getHTMLWebpackPlugins(PAGES),
              new RemoveEmptyScriptsPlugin({
                ignore:  PAGES,
                enabled: isDev === false,
              }),
              new MiniCssExtractPlugin({
                filename: pathdata => {
                  return getCSSDirname(pathdata.chunk.name) + getFilename(pathdata.chunk.name, 'css');
                },
                chunkFilename: isDev ? '[id].css' : '[id].[contenthash].css',
              }),
            ],
          };
          

          我的源文件:

          [src]:
           - index.js
           - index.html
           - help.js
           - help.html
           - [scss]:
           - - app.scss
           - - help.scss
           - - [themes]:
           - - - light.scss
           - - - dark.scss
           - - - blue.scss
          

          构建后:

          [dist]:
           - app.js
           - index.html
           - help$hash.js
           - help$hash.html
           - [css]:
           - - app$hash.css
           - - help$hash.css
           - - [themes]:
           - - - light.css
           - - - dark.css
           - - - blue.css
          

          【讨论】:

            【解决方案9】:

            您可以使用remove-files-webpack-plugin 解决问题№1。

            像这样使用这个插件:

            plugins: [
              new RemovePlugin({
                watch: {
                  test: [
                    {
                      folder: './js',
                      method: (absPath) => new RegExp(/(.*)-([^-\\\/]+)\.js/).test(absPath)
                    }
                  ]
                }
              })
            ]
            
            

            在“监视”模式下(不是正常编译!)它从./js 文件夹中获取所有文件并使用正则表达式/(.*)-([^-\\\/]+)\.js/ 对其进行测试。如果您在理解上有问题,请在regex101(包括单元测试)上分析此正则表达式。

            注意:我是这个插件的创建者。

            【讨论】:

              【解决方案10】:

              对于 Windows 用户

                "scripts": {
                  "build": "npm run clean && webpack --mode production",
                  "clean": "del /f /s /q dist 1>nul"
                }
              

              【讨论】:

                【解决方案11】:

                我只需要停止我的服务器并再次运行yarn serve

                【讨论】:

                  猜你喜欢
                  • 2019-08-13
                  • 2018-01-09
                  • 2018-06-03
                  • 2020-01-07
                  • 2018-09-14
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-31
                  • 2019-02-15
                  相关资源
                  最近更新 更多