mini-css-extract-plugin icon indicating copy to clipboard operation
mini-css-extract-plugin copied to clipboard

Different import order leads to multiple vendor bundles

Open wyntau opened this issue 6 years ago • 10 comments

Condition

Suppose I have a project have two entry file foo-entry.js and bar-entry.js, both of them import es6-promise and normalize.css. Additionally foo-entry may also import other modules, e.g. object-assign.

Now I want to split both shared node_modules es6-promise and normalize.css to shared-vendor.js and shared-vendor.css(use mini-css-extract-plugin to extract out from shared-vendor.js), and split object-assign to foo-vendor.js.

So on foo page, I add shared-vendor.js, foo-vendor.js, foo-entry.js to script tag in html And on bar page, I add shared-vendor.js and bar-entry.js to script tag in html.

Reproduce

To do this, I pass a config array to webpack. the config like below. The only difference is two entry name

var webpack = require('webpack');
var MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = [{
  mode: 'development',
  entry: {
    'foo-entry': './src/foo-entry.js',
  },
  output: {
    filename: '[name].pack.[chunkhash:12].js',
    chunkFilename: 'foo-chunk.[chunkhash:12].js'
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /\/node_modules\/(es6-promise|normalize\.css)\//,
          name: 'shared-vendor',
          filename: 'share-vendor.pack.[chunkhash:12].js',
          chunks: 'initial'
        },
        // here may have other groups with lower priority to split rest modules
      }
    }
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].pack.[chunkhash:12].css'
    })
  ],
  module: {
    rules: [
      {
        test: /\.css/,
        // loader: ['style-loader', 'css-loader'],
        loader: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  }
}, {
  mode: 'development',
  entry: {
    'bar-entry': './src/bar-entry.js',
  },
  output: {
    filename: '[name].pack.[chunkhash:12].js',
    chunkFilename: 'bar-chunk.[chunkhash:12].js'
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /\/node_modules\/(es6-promise|normalize\.css)\//,
          name: 'shared-vendor',
          filename: 'share-vendor.pack.[chunkhash:12].js',
          chunks: 'initial'
        }
        // here may have other groups with lower priority to split rest modules
      }
    }
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].pack.[chunkhash:12].css'
    })
  ],
  module: {
    rules: [
      {
        test: /\.css/,
        // loader: ['style-loader', 'css-loader'],
        loader: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  }
}]
// src/foo-entry.js
import 'normalize.css';
import 'es6-promise/auto';
// src/bar-entry.js, the import order is different from foo-entry.js
import 'es6-promise/auto';
import 'normalize.css'

Expected result

Generate four files.

  • foo-entry.pack.[chunkhash].js
  • bar-entry.pack.[chunkhash].js
  • shared-vendor.pack.[chunkhash].js, include es6-promise
  • shared-vendor.pack.[chunkhash].css, include normalize.css

Actual result

Generate six files. Two shared-vendor.js and two shared-vendor.css with the very same content

  • foo-entry.pack.[chunkhash].js
  • bar-entry.pack.[chunkhash].js
  • shared-vendor.pack.[chunkhash-1].js, include es6-promise
  • shared-vendor.pack.[chunkhash-1].css, include normalize.css
  • shared-vendor.pack.[chunkhash-2].js, include es6-promise
  • shared-vendor.pack.[chunkhash-2].css, include normalize.css

Search and find

  • If I do not use mini-css-extract-plugin, replace it with style-loader(the commented line on above webpack config), only one shared-vendor.pack.[chunkhash].js and shared-vendor.pack.[chunkhash].css will be generated.
  • If I change import order in bar-entry.js, firstly import normalize.css and then import es6-promise like foo-entry, it will also generate only one shared-vendor.pack.[chunkhash].js and shared-vendor.pack.[chunkhash].css.

Problem

  • Does mini-css-extract-plugin change the chunkhash compute algorithm?
  • Why same content have different chunkhash?
  • Why different import order will generate different chunkhash but same content ?
  • Does my this usage have problem? How can I do to split both shared modules to shared-vendor, and the rest modules to [name]-vendor?

Reproduce project

reproduce.zip To mininal reproduce, I removed all other modules. So foo-entry and bar-entry will only import two modules es6-promise and normalize.css and have no any other code

Environment

  • Node v8.11.1
  • webpack 4.8.1
  • mini-css-extract-plugin 0.4.0
  • OS Mac 10.12.6

wyntau avatar May 09 '18 01:05 wyntau

@Wyntau Thanks for issue, we known this problem and already fixed this problem, just wait improve documentation https://github.com/webpack/webpack.js.org/issues/2096. Thanks! Feel free to feedback!

alexander-akait avatar May 09 '18 09:05 alexander-akait

@Wyntau I will leave open because looks like you have other issue around code splitting, right?

alexander-akait avatar May 09 '18 09:05 alexander-akait

Does it mean I can now replace all [chunkhash] with [contenthash] safely in webpack 4.8.1?

wyntau avatar May 09 '18 09:05 wyntau

@Wyntau yes, [chunkhash] for backward compatibility, use always [contenthash], in webpack@5 it is right way to create long term caching

alexander-akait avatar May 09 '18 09:05 alexander-akait

@evilebottnawi I canged all [chunkhash] to [contenthash], and only one shared-vendor.pack.[contenthash].css will generated, But still two shared-vendor.pack.[contenthash].js with same content.

image

  1. When I use mini-css-extract-plugin, the bar-entry's import modules order will still affect the [contenthash] like above described
  2. If I use style-loader, only one shared-vendor.pack.[contenthash].js(included es6-promise and normalize.css) will generated.

wyntau avatar May 09 '18 09:05 wyntau

@Wyntau looks like problem with splitChunks, ~~can you create minimum reproducible test repo?~~ sorry, found in original post. ~~It is hard to solve based only on configuration~~

alexander-akait avatar May 09 '18 10:05 alexander-akait

@evilebottnawi reproduce.zip

this is the newest zip

npm install
npx webpack

wyntau avatar May 09 '18 10:05 wyntau

hi @evilebottnawi , do you have any idea why will two vendor files with the same content be generated?

wyntau avatar May 19 '18 00:05 wyntau

The [chunkhash] change is to be expected, use [contenthash] instead. Did you resolve the issue about multiple vendor bundles being generated ?

michael-ciniawsky avatar Aug 24 '18 13:08 michael-ciniawsky

@michael-ciniawsky not yet

wyntau avatar Aug 26 '18 08:08 wyntau