background

I believe that many children in the interview will often be asked “have you manually configured webpack”, “webpack basic configuration” and other questions. This article takes you through common webPack basics (this article is based on the React stack).

I. Dependency package installation

1. Dependency packages of WebPack

  1. webpack Webpack Core package This article relies on version 4.x
  2. webpack-cli Webpack command line tool that relies on the WebPack core package
  3. webpack-dev-server Development environment code live reloading vs. Hot Module replacement
  4. webpack-merge The development and production environments incorporate the WebPack base configuration
npm install webpack webpack-cli webpack-dev-server webpack-merge -D

Copy the code

2. Babel dependency packages

  1. @babel/core The JS code is analyzed into AST, which is convenient for each plug-in to analyze the syntax for corresponding processing
  2. @babel/preset-env You can use the latest JS syntax without having to manage syntactic conversions or browser polyfills required by the target environment
  3. @babel/preset-react React support, such as JSX syntax
  4. @babel/plugin-proposal-class-properties
  5. core-js Supports new ES6 + features, such as Promise, Set, and Iterator
  6. babel-loader Parsing js
npm install @babel/core @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties core-js@3 babel-loader -D 

Copy the code

Starting with Babel V7, all stage presets written for features of the standard proposal stage have been deprecated, and are officially removed @babel/preset-stage-x

3. Dependency packages related to style processing (here take less as an example)

  1. autoprefixer Add prefixes to CSS properties for different browsers
  2. css-loader
  3. postcss
  4. postcss-loader CSS parses into an AST that JS can manipulate, and calls plug-ins to process the AST to get the result
  5. less
  6. less-loader
npm install autoprefixer css-loader postcss postcss-loader less less-loader -D

Copy the code
  1. This can be done by replacing autoprefixer with CSSNext.
  2. The CSSNext plug-in allows developers to use new features that may be added to future versions of CSS in their current projects.
  3. Cssnext is responsible for translating these new features into syntax that can be used in current browsers. From an implementation perspective, CSSNext is a collection of PostCSS plug-ins related to future versions of CSS. For example, cssNext already includes the use of Autoprefixer, so using CSSNext eliminates the need for Autoprefixer.

4. File (including picture, video, audio and font files) processing related dependency packages

  1. file-loader
  2. url-loader
npm install file-loader url-loader -D

Copy the code
  1. File-loader returns the URL of the image
  2. Url-loader can process images based on the limit attribute. When the size of an image is smaller than limit (byte), the image is transferred to Base64. When the size is larger than limit, the image is processed by file-loader.

5. Common plugins dependencies (important)

  1. clean-webpack-plugin Clearing the package file
  2. copy-webpack-plugin Copy some static resources to a specific folder
  3. html-webpack-plugin Create an HTML file and automatically insert the webpack static file into it
  4. mini-css-extract-plugin Extract the CSS into a separate file
  5. optimize-css-assets-webpack-plugin Compress CSS
  6. terser-webpack-plugin Compression js
npm install clean-webpack-plugin copy-webpack-plugin html-webpack-plugin mini-css-extract-plugin optimize-css-assets-webpack-plugin terser-webpack-plugin -D

Copy the code
  1. The HTML-webpack-plugin can be configured multiple times and is common in multi-entry packaging
  2. The extract-text-webpack-plugin is used to replace the extract-text-webpack-plugin, which has advantages over the extract-text-webpack-plugin. (1) asynchronous loading (2) no repeated compilation (performance) (3) Simpler configuration (4) CSS specific development
  3. Terser-webpack-plugin is used to replace uglifyjs-webpack-plugin because UglifyJS does not support ES6 syntax

Webpack configuration

0. Front path configuration file

Add the config.js configuration file

const path = require('path');

const resolve = (dir) => path.resolve(__dirname, dir);

const config = {

// The path specified by publicPath is prefixed to all urls, such as link tags, script tags, and img tags in HTML files

  pubicPath: '/'.

  template: resolve('.. /public/index.html'),

  entry: resolve('.. /src/index.js'),

// Package file location

  path: resolve('.. /dist'),

};



module.exports = config;



Copy the code

1. Basic configuration

Add the basic webpack.config.js configuration file as follows:

const path = require('path');

const HtmlWebpackPlugin = require('html-webpack-plugin');

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const config = require('.. /config');



const webpackConfig = {

  devtool: false, // This option controls whether and how it is generatedsource map

  resolve: {

    alias: {

      demo: path.join(__dirname, '.. /src/components'),

    },

// Try to resolve file suffixes in order. If there are multiple files with the same name but different suffixes, WebPack will resolve files with the suffixes listed at the top of the array and skip the rest.

    extensions: [The '*'.'.js'.'.jsx'.'.less'.'.css'].

  },

// Entry configuration

  entry: {

    bundle: config.entry,

    vendor: ['react'.'react-dom'.'react-router-dom'].

  },

// Output configuration

  output: {

    filename: '[name]-[hash:8].js'.

    // chunkFilename: '[name]-[chunkhash:8].js'.

    path: config.path,

  },

  module: {

// Parser configuration

    rules: [

      {

        test: /\.(js|jsx)$/,

        use: {

          loader: 'babel-loader'.

          options: {

            presets: [

              [

                '@babel/preset-env'.

                {

                  targets: {

// Preset -env is not required if the version is larger than the relevant browser version

                    edge: '17'.

                    firefox: '60'.

                    chrome: '67'.

                    safari: '11.1'.

                  },

                  corejs: '3', // Declare the corejs version, which provides support for new es6+ syntax and features

// Import methods according to the ES6+ syntax used in the code logic, not all

                  useBuiltIns: 'usage'// useBuiltIns specifies whether to enable automatic support for polyfill, which automatically adds the poly-fill required for each file.

                },

].

              '@babel/preset-react'.

].

            plugins: ['@babel/proposal-class-properties'], // Resolve Supportfor the experimental syntax 'classProperties' isn't currently enabled

          },

        },

        exclude: /node_modules/,

      },

      {

        test: /\.(css|less)$/,

        use: [

          {

            loader: MiniCssExtractPlugin.loader,

            options: {

// Enable hot updates only in development mode

              hmr: process.env.NODE_ENV === '
development',

// If module hot update does not work, reload all styles

              reloadAll: true,

            },

          },

          '
css-loader',

          {

            loader: '
postcss-loader',

            options: {

              postcssOptions: {

                ident: '
postcss', // specify which add-ons are used for postCSS

                plugins: [

// The plugin here is only for PostCSS

                  require('
autoprefixer')(), // to introduce the prefixed plug-in, the second empty parenthesis is to execute the plug-in

].

              },

            },

          },

          '
less-loader',

].

      },

      {

test: /\.(png|jpe? g|gif|svg)(\? . *)? $/,

        use: [

          {

            loader: '
url-loader',

            options: {

Limit: 100, // If the value does not exceed 100 bytes, it is converted to Base64 bits

              name: '
assets/img/[name].[ext]', // Image output path

            },

          },

].

      },

      {

test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/,

        use: [

          {

            loader: '
url-loader',

            options: {

              limit: 10000,

              name: '
assets/blob/[name].[ext]', // Audio/video output path

            },

          },

].

      },

      {

test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/,

        use: [

          {

            loader: '
url-loader',

            options: {

              limit: 10000,

              name: '
assets/font/[name].[ext]', // font output path

            },

          },

].

      },

].

  },

// Plug-in configuration

  plugins: [

    new HtmlWebpackPlugin({

      template: config.template,

Inject: true, // The inject option has four values: true,body(script tag at the bottom of body),head,false(do not insert JS file)

      filename: '
index.html',

      minify: {

HTML / / compression

RemoveComments: true, // removeComments

CollapseWhitespace: true, // Remove whitespace

      },

    }),

    new MiniCssExtractPlugin({

      filename: process.env.NODE_ENV === '
development'? 'assets/style.css':'assets/style.[hash:8].css', // configure the style file output path

    }),

].

};



module.exports = webpackConfig;



Copy the code
  1. Plug-ins included with @babel/preset-env will support all the latest JS features (ES2015,ES2016, etc., excluding the stage stage) and turn them into ES5 code without any configuration. For example, if the code uses new Optional Chaining or Nullish coalescing Operator that is still in the stage, configure @babel/ PRESET -env, The conversion throws an error and requires an additional plug-in.
  2. After babel7.4, @babel/polyfill is officially not recommended, and core-js@3 is used instead
  3. When configuring filename, the MiniCssExtractPlugin differentiates the name of the style file between the development environment and the production environment. The reason is that in the development environment, if the name of the style file is not fixed, the style cannot be updated automatically
  4. Import img from ‘./image.png’. Import CSS styles, for example, backgorund: URL (‘./image.png’).

2. Add the dev environment configuration file based on the basic configuration

const webpack = require('webpack');

const merge = require('webpack-merge');

// const CopyWebpackPlugin = require('copy-webpack-plugin');

const baseConfig = require('./webpack.config');

const config = require('.. /config');



const devConfig = merge(baseConfig, {

  mode: 'development'.

  devtool: 'inline-source-map'.

  devServer: {

    hot: true, // hotOnly after modificationcommandAfter +s, the page is not refreshed, but needs to be refreshed manually

    inline: true.

    disableHostCheck: true.

    contentBase: config.path,

    compress: true.

    host: 'localhost'.

    port: 8080,

    overlay: true.

    publicPath: config.pubicPath,

    proxy: {

      '/api': {

        target: 'http://xxx.meituan.com'.

        changeOrigin: true.

      },

    },

  },

  output: {

    publicPath: '/'.

  },

  plugins: [

    new webpack.HotModuleReplacementPlugin()

    // new CopyWebpackPlugin([

/ / {

    //     from: path.resolve(__dirname, '.. /static'),

    //     to: config.staticSubDirectory,

    //     ignore: ['*'].

    //   },

    // ]),

].

});



module.exports = devConfig;



Copy the code
  1. After hotOnly is modified, the page is not refreshed after command+s is executed. You need to manually refresh the page
  2. When mode is development, NamedModulesPlugin, NamedChunksPlugin, and DefinePlugin configurations can be omitted
  3. In the development environment, you can always use webpack-dev-server for hot updates. As you can see from the above configuration, hot updates are already in effect, but each change reloads the entire page instead of local updates. The solution is to add the following code at the entrance:It is very important) :
if (module.hot) { 

Module.hot.accept (App, () => {//App represents the DOM that needs hot updates

     render(App); 

   });

}

Copy the code
  1. The 5.x version of Wepback has been released, but I tried the hot update of 5.x version and it didn't work, so I haven't found a solution yet

3. Add the PROD environment configuration file based on the basic configuration

const merge = require('webpack-merge');

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

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

const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

// const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

const baseConfig = require('./webpack.config');

const config = require('.. /config');



module.exports = merge(baseConfig, {

  mode: 'production'.

  output: {

    publicPath: config.pubicPath,

  },

  plugins: [

    new CleanWebpackPlugin(),

// New BundleAnalyzerPlugin(), // after the package is finished, a service is started to view the size of the package and the package contents in the browser

].

  optimization: {

    minimizer: [

/ / js compressed

      new TerserWebpackPlugin({

        parallel: true.

        exclude: /\/node_modules/,

        extractComments: false// If this option istrueAn xxx.js.license. TXT file is generated to store comments in a specific format

        terserOptions: {

          warnings: false.

          compress: {

            unused: true.

            drop_debugger: true// Delete the debugger

            drop_console: true// Delete console

          },

        },

      }),

/ / CSS compression

      new OptimizeCssAssetsPlugin({

        cssProcessorOptions: { safe: true, discardComments: { removeAll: true}},

      }),

].

  },

});



Copy the code
  1. Mode of production can be omitted when NoEmitOnErrorsPlugin, DefinePlugin, TerserPlugin, ModuleConcatenationPlugin configuration

Start the command

"scripts": {

  "start""NODE_ENV=development webpack-dev-server --inline --config webpack.dev.js".

  "build""NODE_ENV=production webpack --config webpack.prod.js".

}

Copy the code

You may refer to detailed code https://github.com/XUGAOBO/webpack-demo