In the first two basic and advanced configuration, we learned how to build pack configuration Webpack to achieve different purposes, including frequent mention of the entry, loader, the plugin, the module, the chunk, bundle nouns, such as what they are? What are the differences and connections?

The module, the chunk and bundle

Webpack is described on the website as follows:

Webpack is a static module packaging tool for modern JavaScript applications. When WebPack works with an application, it internally builds a dependency graph that maps to each module required for the project and generates one or more bundles.

We can also see a picture on the homepage of the official website to illustrate the role of Webpack:And we can see that in this picturemodule.chunkandbundleThe difference and connection between:

  • The left-hand side of the equation can be interpreted asmodule, it isIndividual source files.In the world of Webpack, everything is modular, anything that can be referenced is a module.
  • The middle part ischunk.A combination of multiple modules, such as:entry.import().splitChunkAnd so on.chunkDo dependency analysis.
  • The right-hand side isbundle, i.e.,The final output file.

The structure of the Webpack

Let’s take a look at the complete structure of the configuration object output from the Webpack configuration file:

|-- webpack.config.js
    |-- entry Define the entry to build the dependency graph
    |-- output Define the name and location of the output bundle
    |-- module # define loader
    |-- plugins # define plug-in
    |-- mode Select develop or build mode
    |-- devServer The development environment starts the service
    |-- optimization Configure optimization items manually
Copy the code

Entry (entrance)

Entry Point __ indicates which module WebPack should use as a starting point for building its internal dependency graph. Once at the entry point, WebPack finds out which modules and libraries are (directly and indirectly) dependent on the entry point. In the previous two sections, you can configure entry in two ways:

Single entry

Usage: entry: string | [string]

module.exports = {
  entry: './path/to/my/entry/file.js'
};
Copy the code

Multiple entry

Usage: entry: {< entryChunkName > string | [string]} | {} each pair of key/value pair is a separate entrance, the separation of independent dependency graph, thus can make a difference in the chunk.

module.exports = {
  entry: {
    app: './src/app.js'.adminApp: './src/adminApp.js'}};Copy the code

Why do you need multiple entrances?

If our program is a multi-page program, we need to configure multiple entries in Webpack. In a multi-page application, the server pulls a new HTML document to the client. The page reloads the new document, and the resource is re-downloaded. However, use optimization.splitchunks to create bundles for application code shared between pages. Due to the increasing number of entry points, multi-page applications can reuse large amounts of code/modules between multiple entry points, which can greatly reduce the amount of duplicate code and the volume of the final package.

The output (output)

The Output attribute tells WebPack where to export the bundles it creates and how to name those files. Note that even though more than one entry starting point can exist, only one output configuration can be specified.

Single entry

module.exports = {
  output: {
    filename: 'bundle.js',}};Copy the code

This configuration outputs a single bundle.js file to the dist directory.

Multiple entry

If more than one chunk is created in the configuration, placeholders should be used to ensure that each file has a unique name.

module.exports = {
  entry: {
    app: './src/app.js'.search: './src/search.js'
  },
  output: {
    filename: '[name].js'.path: __dirname + '/dist'}};Copy the code

loader

Webpack only understands JavaScript and JSON files. Loader enables WebPack to process other types of files and convert them into valid modules for use by applications and to be added to dependency diagrams. In the webpack configuration, loader has two properties:

  1. test: Identifies which files will be converted.
  2. use: defines which loader should be used during conversion.
module.exports = {
  module: {
    rules: [{test: /\.css$/, use: 'css-loader' },
      { test: /\.ts$/, use: 'ts-loader'}}};Copy the code

The plugin (plug-in)

Loaders are used to transform certain types of modules, while plug-ins can be used to perform a wider range of tasks. Including: package optimization, resource management, injection of environment variables. Plug-ins are designed to solve other things that loader cannot implement. To use a plug-in, simply require() it and add it to the plugins array. Most plug-ins can be customized with options. You can also use the same plug-in multiple times in a configuration file for different purposes, creating an instance of the plug-in by using the new operator.

onst HtmlWebpackPlugin = require('html-webpack-plugin'); // Install via NPM
const webpack = require('webpack'); // Used to access built-in plug-ins

module.exports = {
  module: {
    rules: [{test: /\.txt$/, use: 'raw-loader'}},plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'}})];Copy the code

The principle of the author

The Webpack plug-in is a JavaScript object with the Apply method. The Apply method is called by the Webpack Compiler and the Compiler object is accessible throughout the compile life cycle. ConsoleLogOnBuildWebpackPlugin.js:

const pluginName = 'ConsoleLogOnBuildWebpackPlugin';

class ConsoleLogOnBuildWebpackPlugin {
  apply(compiler) {
    compiler.hooks.run.tap(pluginName, compilation= > {
      console.log('Webpack build process begins! '); }); }}module.exports = ConsoleLogOnBuildWebpackPlugin;
Copy the code

Mode (mode)

Setting the mode parameter by selecting one of development, Production, or None enables the optimization built into WebPack for that environment. The default value is production. Usage: string = ‘production’ : ‘none’ | ‘development’ | ‘production’

model function
development willDefinePluginprocess.env.NODE_ENVIs set todevelopmentIs the module andchunkEnable a valid name.
production willDefinePluginprocess.env.NODE_ENVIs set toproduction. For the module andchunkEnable deterministic obfuscated names,FlagDependencyUsagePlugin.FlagIncludedChunksPlugin.ModuleConcatenationPlugin.NoEmitOnErrorsPluginTerserPlugin
none Do not use any default optimization options

Optimization (optimization)

Starting with WebPack 4, different optimizations will be performed depending on the mode you choose, but all optimizations can be manually configured and rewritten. Let’s take a look at the optimization related plugins that we used for the configuration in the previous two sections.

Minimizer (compressed)

Output code is compressed by providing one or more custom TerserPlugin instances that override the default compression tool (Minimizer). [TerserPlugin] or [function (compiler)]

const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  optimization: {
    minimizer: [
      JS / / compression
      new TerserPlugin({
        cache: true.parallel: true.sourceMap: true.// If source-maps is used in production, it must be set to true
        terserOptions: {
          // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions}}),/ / compress CSS
      new OptimizeCSSAssetsPlugin({}),
    ],
  }
};
Copy the code

splitChunks

Prior to Webpack 4, chunks were connected via dependency maps, using the CommonsChunkPlugin to avoid overlapping chunks of dependency, but there was no other way to optimize them further. After Webpack 4, CommonsChunkPlugin was removed and replaced with Optimization.splitchunks to customize the fragmentation of code blocks to achieve finer fragmentation and reuse and better load on demand.

The default value

Webpack will automatically split chunks based on the following criteria:

  • New blocks can be shared, or modules can come from the node_modules folder
  • New block >= 20KB (before min + gz)
  • When loading blocks on demand, the maximum number of parallel requests is <= 30
  • The maximum number of parallel requests at initial page loading is <= 30

The custom

Let’s review the configuration of optimization.splitchunks in the previous two sections:

chunks: Selects which blocks to optimize

Values: string = ‘all’ | ‘async’ | ‘initial’

  • 'async': asynchronous chunk. Only files that are imported asynchronously are processed
  • 'initial': chunk. Files that are imported asynchronously are not processed
  • 'all': Blocks can also be shared between asynchronous and non-asynchronous blocks

cacheGroups: Cache grouping

The cacheGroups structure is an object where each attribute corresponds to a configuration of a chunk. The key is the name of the chunk.

  • name:chunkThe name of the
  • priority: A module can belong to more than one cache group, and optimization will preferentially select the cache group with the highest cache grouppriority, the higher the value, the higher the priority
  • test: controls the module selection for this cache group, omitting it selects all modules. It can match absolute module resource paths or block names. When a block name is matched, all modules in the block are selected
  • minSize: Size limit of the module
  • minChunks: The minimum number of module reuse times. Only the value greater than or equal to this value will be extracted
module.exports = {
 optimization: {
    // Split code blocks
    splitChunks: {
      chunks: 'all'.// Cache grouping
      cacheGroups: {
          // Third party module
          vendor: {
              name: 'vendor'./ / the chunk name
              priority: 1.// The permission is higher
              test: /node_modules/,
              minSize: 0.// Size limit
              minChunks: 1  // At least several times
          },

          // Public module
          common: {
              name: 'common'./ / the chunk name
              priority: 0./ / priority
              minSize: 0.// Public module size limit
              minChunks: 2  // The public module has been reused at least several times}}}}};Copy the code