Webpack configuration from scratch

Time:2021-3-6
  • Since webpack5 has just been released, the relevant ecology is not mature. Here, take the version of webpack4 as an example,
  • To avoid the compatibility problem of plug-in versions, I will specify the version number when installing individual NPM dependencies.
  • This paper is only used for webpack learning. In actual projects, it is more recommended to use mature scaffolding, and then customize the transformation according to the learning results.
  • The sample code has been uploaded to GitHub:webpack-demo

1、 Foundation

1. Catalog file

Create a new folder as a sample project, and run the command to initialize the project root directorypackage.json

npm init -y

Then create an empty file in the following directory:
Webpack configuration from scratch

2. Entry and output

  • Install webpack dependencies
npm i -D [email protected] [email protected]
  • /config/webpack.base.config.jsWrite content:
const path = require('path')

module.exports = {
  Entry: {// entry configuration
    app: './src/index.js'
  },
  Output: {// export configuration
      filename: 'js/[name].[contenthash:8].js',
      path: path.resolve(__dirname, '../dist'),
  }
}
  • package.jsonWrite script command in
"script": {
  "build": "webpack --config ./config/webpack.base.config.js"
}
  • Then you can run to see the effect:
npm run build
  • Normally, the dist folder will be generated in the root directory of the project, which is the packaged file.

Webpack configuration from scratch

  • hashchunkhashcontenthashThe differences between them are as follows:

hashIt is generated every time when packaging, and all files share the same hash;
chunkhashAccording to different entry files, the dependency file is parsed, the corresponding chunk is constructed, and the corresponding hash value is generated. In the production environment, some public libraries will be separated, as long as the public library does not change, the generated hash value will not change.
contenthashIt is related to the content of the file. If the content remains unchanged, the generated hash value will remain unchanged.

3. HTML template HTML webpack plugin

Use the HTML webpack plugin plug-in to configure the association of HTML template files, so that the packaged JS and CSS will be automatically introduced into HTML, and you can access the HTML file to view the effect.

  • To install the plug-in:
npm i -D [email protected]
  • /config/webpack.base.config.jsAdd plugins and configure HTML webpack plugin
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: ......,
  output: ......,
  //Add here
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      inject: 'body',
      hash: false
    }),
  ],
}
  • /public/index.htmlWrite code:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge, chrome=1">
  <title>Title</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>
  • /src/ index.js Write code:
console.log ('little Prince ')
  • Finally, run the command packagenpm run build, which will be generated under dist after packaging index.html Open the HTML to view the console output.

4. JS compiler react, Babel

Any framework is OK. The purpose is to learn the configuration of webpack. Take react as an example.

  • Install react dependency:
npm i -S react react-dom
  • /src/index.jsReplace with the following code:
import React from 'react'
import ReactDOM from 'react-dom'

function App () {
  return (
    <div>
      < div classname = "test" > Little Prince < / div >
    </div>
  )
}

ReactDOM.render(
  <App/>, 
  document.getElementById('root')
)
  • Add Babel:

Because the JSX syntax used in react is not the JS standard language syntax, we need to use Babel plug-in to transcode. Of course, Babel is far more useful than that. We also need Babel to convert ES6 + code into Es5 code with better compatibility.
Installation of Babel depends on:

npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react
  • babel.config.js Write code:
module.exports = {
  presets: [
    '@babel/preset-env', 
    '@babel/preset-react'
  ],
}
  • /config/webpack.base.config.jsAdd module and configure Babel’s rules:
const path = require('path')

module.exports = {
  entry: ......,
  output: ......,
  plugins: ......,
  //Add the code here
  module: {
    rules: [
      {
        test: /\.(js|jsx)?$/,
        options: {
          cacheDirectory: true
        },
        loader: 'babel-loader'
      }
    ]
  }
}
  • functionnpm run build, and then open the packaged index.html See the effect.

5. Configure separate webpack merge

In the webpack configuration, you can specify the mode attribute to divide the running environment into development and production,
Using the webpack merge plug-in, we can use different webpack configurations for different mode environments. The plug-in helps us to intelligently merge configurations.

  • Installation dependency:
npm i -D [email protected]
  • /config/webpack.dev.config.jsWrite code:
const merge = require('webpack-merge')
const common = require('./webpack.base.config')

module.exports = merge(common, {
  mode: 'development',
  output: {
    filename: 'js/[name].js',
  },
})
  • /config/webpack.prod.config.jsWrite code:
const merge = require('webpack-merge')
const common = require('./webpack.base.config')

module.exports = merge(common, {
  mode: 'production',
})
  • package.json Modify the scripts command in
"scripts": {
  "start": "webpack --config ./config/webpack.dev.config.js",
  "build": "webpack --config ./config/webpack.prod.config.js"
},

Pay attention to the webpack.base.config . JS is replaced by webpack.prod.config .js。
In this way, it is divided into development environment and production environment.

6. Clear webpack plugin

Using the clean webpack plugin, you can automatically delete the last packaged dist folder before the build is packaged to prevent the generation of redundant files.

  • Installation dependency:
npm i -D clean-webpack-plugin
  • /config/webpack.prod.config.jsAdd plugins configuration in
const merge = require('webpack-merge')
const common = require('./webpack.base.config')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = merge(common, {
  mode: 'production',
  //Add the code here
  plugins: [
    new CleanWebpackPlugin(),
  ],
})
  • npm run buildCheck the dist folder to see if there are any JS files left over before.

7. Hot update webpack dev server

Using the webpack dev server plug-in, when the webpack is running, a local server will be automatically started to run the packaged HTML file. With the hot update, the real-time viewing effect after code changes can be realized.

  • Installation dependency:
npm i -D [email protected]
  • /config/webpack.dev.config.jsAdd devserver and plugins configuration in
const merge = require('webpack-merge')
const common = require('./webpack.base.config')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = merge(common, {
  mode: 'development',
  output: {
    filename: 'js/[name].[hash:8].js',
  },
  //Add the code here
  devServer: {
    open: true,
    port: 9000,
    compress: true,
    hot: true,
    inline: true,
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],
})
  • package.jsonModify the start command of scripts in
"start": "webpack-dev-server --inline --progress --config ./config/webpack.dev.config.js",
  • npm startSee the effect.

8. Source tracking devtool, source map

Devtool is used to configure the source map option to help us trace the original source code when debugging. There are multiple source map formats to choose from. Please refer tofileConsidering the construction speed and use effect, we generally choose soap module Eval source map, which is relatively balanced in all aspects.

  • /config/webpack.dev.config.jsAdd devtool configuration in
module.exports = merge(common, {
  mode: 'development',
  Devtool: 'soap module Eval source map' // just add it here
  output: ......,
  devServer: ......,
  plugins: ......,
})
  • /src/index.jsAdd a console statement in
import React from 'react'
import ReactDOM from 'react-dom'

console.log (123) // just add it here

function App () {
  ......
}
......
  • Find the print result line in the chrome console, click the file path on the right side of the line to view the source code, and understand the function of source map by comparing the source code when adding and not adding devtool.

9. Style dependent loader

Style loader is used to create style tags and introduce CSS code, which cannot be used alone;
CSS loader is used to parse CSS files and generate CSS code for style loader;
Less loader is used to convert less files into CSS files for use by CSS loader;

  • Installation dependency:
npm i -D style-loader css-loader less less-loader
  • Creating files in SRC folderindex.less
@color: red;

.test {
  color: @color;
}
  • /src/index.jsIntroduce the less to use:
import React from 'react'
import ReactDOM from 'react-dom'
import './index.less'

function App () {
  return (
    <div>
      < div classname = "test" > Little Prince < / div >
    </div>
  )
}

ReactDOM.render(
  <App/>, 
  document.getElementById('root')
)
  • /config/webpack.base.config.jsTo configure the loader:
module.exports = {
  module: {
    rules: [
      ......
      //Then, add the following code
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
    ]
  }
}

The use array in rules is parsed from right to left. You should pay attention to the order.

10. CSS toolset postcss

postcssIt is a collection of tools that allow JS plug-ins to transform styles;
postcss-loaderThe loader is used to further process CSS in webpack;
autoprefixerIt is a plug-in of postcss. With postcss loader, it can automatically add browser prefix to CSS style to be compatible with low version browsers;
browserlistIt is used to specify the target browser range of the project, which can be recognized by autoprefixer and Babel, and make compatible adaptation according to the target browser range.

  • Installation dependency:
npm i -D postcss postcss-loader autoprefixer browserlist
  • /src/index.lessAdd style:
@color: red;

.test {
  color: @color;
  display: flex;
  justify-content: center;
}
  • /config/webpack.base.config.jsModify the loader configuration of CSS and less in
module.exports = {
  module: {
    rules: [
      ......
      //Modify the loader configuration of CSS and less
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader']
      },
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
      },
    ]
  }
}
  • Project root new filepostcss.config.js
module.exports = {
  plugins: {
    autoprefixer: {}
  }
}
  • package.jsonAdd browserlist configuration in
{
  "dependencies": ......,
  "devDependencies": ......,
  ---Add here---
  "browserslist": [
    "> 1% in CN",
    "last 2 versions"
  ]
}
  • npm startRun the chrome developer tool to check the CSS style of the text and see if the relevant flex styles are automatically prefixed with the browser.

11. File loader, URL loader

File loader is used to package static files and associate the import path with JS;
url-loaderIt is used to process the packaging of image resources. When the size is lower than the specified size, the resources will be converted to Base64 format for use. In other cases, the processing is the same as that of file loader.

  • Installation dependency:
npm i -D file-loader url-loader
  • /config/webpack.base.config.jsAdd loader configuration in
module.exports = {
  module: {
    rules: [
      ......
      //Then, add the following code
      {
        test: /\.(jpe?g|png|gif)$/i,
        options: {
          esModule: false,
          Limit: 4096, // images below 4K will be converted to Base64 format
        },
        loader: 'url-loader',
      },
      {
        Test: / \ (woff | woff2 | EOT | TTF | OTF) $/ I, // process font file
        options: {
          esModule: false
        },
        loader: 'file-loader'
      },
    ]
  }
}

12. CSS separation Mini CSS extract plugin

Mini CSS extract plugin is used to separate the packaged CSS. When webpack is packaged, the default is to integrate CSS into JS by creating style tags dynamically. This plug-in can separate CSS, reduce unnecessary JS code and DOM operation, and improve page loading performance.

  • Installation dependency:
npm i -D mini-css-extract-plugin
  • /config/webpack.base.config.jsTo configure plugins and loaders in the
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  plugins: [
    ......,
    //Then, add it here
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:8].css',
      chunkFilename: 'css/[id].[contenthash:8].css',
      ignoreOrder: true
    }),
  ],
  module: {
    rules: [
      //Modify the loaders of CSS and less and replace the style loader
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']
      },
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader']
      },
    ]
  }
}
  • npm run buildCheck whether the package directory generates a separate CSS file.

13. Shortcut alias

Alias is a built-in property of webpack, which is used to specify the shortcut path ID. after configuration, it is convenient to write the import path.

  • /config/webpack.base.config.jsInside configuration:
module.exports = {
  entry: ......,
  output: ......,
  //Then, add it here
  resolve: {
    alias: {
      '@': path.resolve(__dirname, '../src'),
    }
  },
}
  • /src/index.jsModify the import path of the file:
import './ index.less 'replace with import '@/ index.less '
  • npm startCheck for normal operation

2、 Advanced

1. JS compression terser webpack plugin

terser-webpack-pluginIt is used for code compression and code obfuscation of JS. It supports ES6 + better and replaces uglifyjs webpack plugin.

  • Installation dependency:
npm i -D [email protected]
  • /config/webpack.prod.config.jsAdd configuration in
const TerserWebpackPlugin = require('terser-webpack-plugin')

module.exports = merge(common, {
  mode: 'production',
  plugins: ......,
  //Add here
  optimization: {
    minimize: true,
    minimizer: [
      new TerserWebpackPlugin({
        Parallel: true, // use multi process to speed up construction
        Extractdocuments: false, // prohibit generating license file
        terserOptions: {
          Compress: {// delete console.log code
            drop_console: true,
            pure_funcs: ['console.log']
          },
          output: {
            Comments: false // delete comment code
          }
        }
      }),
    ],
  },
})

2. CSS compression optimize CSS assets webpack plugin

optimize-css-assets-webpack-pluginThe plug-in is used to compress the CSS file, and cssno is used by default.

  • Installation dependency:
npm i -D optimize-css-assets-webpack-plugin
  • /config/webpack.prod.config.jsAdd plugins to
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = merge(common, {
  plugins: [
    ......,
    //Then, add it here
    new OptimizeCssAssetsPlugin(),
  ]
})
  • npm run buildCheck whether the packaged CSS file has been compressed.

3. Split chunks

Split chunks is used for code separation, which is conducive to performance optimization. The judgment principle of module separation: large volume, stable and unchanged.

  • The browser will cache the file after loading it, and read it directly from the local cache when loading the file next time, so as to speed up the access speed.
  • By default, webpack will package the code introduced by import synchronization into one file, while split chunks can be used to separate the file into multiple files.
  • The separation of stable code can ensure that the separated files remain unchanged after each package, so that the separated files can be cached by the browser and the cache will not be invalid after the project is updated and released. This is the main function of split chunks.
  • /config/webpack.pord.config.jsAdd split chunks:
module.exports = merge(common, {
  optimization: {
    ......,
    //Then, add it here
    splitChunks: {
      chunks: 'all',
      maxAsyncRequests: 8,
      maxInitialRequests: 6,
      minSize: 10000,
      cacheGroups: {
        React: {// separate react and react DOM
          name: 'chunk-react',
          test: /[\/]node_ Modules [\ \ /] (react | react DOM) [\ \ /] /, // matching rules
          Priority: 20 // match priority
        },
        Vendors: {// other NPM dependencies (production environment)
          name: 'chunk-vendors',
          test: /[\/]node_modules[\/]/,
          priority: -10,
          chunks: 'initial'
        },
        Common: {// component public detach
          name: 'chunk-common',
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    }
  },
})
  • /config/webpack.dev.config.jsAdd chunkfiles to
module.exports = merge(common, {
  output: {
    filename: 'js/[name].js',
    //Add here
    chunkFilename: 'js/[name].js',
  },
})
  • /config/webpack.base.config.jsAdd chunkfiles to
module.exports = {
  output: {
    filename: 'js/[name].[contenthash:8].js',
    path: path.resolve(__dirname, '../dist'),
    //Add here
    chunkFilename: 'js/[name].[contenthash:8].js',
  },
}
  • Chunkfilename is used to configure the file name separated by split chunks
  • npm run buildCheck the packaged file, pack it several times, and compare whether the separated chunk file name has changed.

4. Custom constant defineplugin

DefinePluginWebpack is a built-in plug-in that allows you to create a global constant that can be configured at compile time. After configuration, you can use this constant in your code.

  • /config/webpack.base.config.jsAdd plugins to
const webpack = require('webpack')

module.exports = {
  plugins: [
    ......,
    //Then, add it here
    new webpack.DefinePlugin({
      VERSION_ H5: + new date() // version added here_ H5
    }),
  ]
}

It should be noted that if you need to use the original quotation marks when assigning a value of string type to a defined constant, you can wrap double quotation marks with single quotation marks or use the JSON.stringify Packages, such as’ ABC ‘or JSON.stringify (‘abc’)

  • /src/ index.js Add a print statement in:
//Just find the right place to add it
console.log(VERSION_H5)
  • NPM start to check whether the output of chrome console meets the expectation

5. Style isolation CSS modules

CSS modules is a modular solution to prevent CSS style pollution.
Next, configure module.css Or module.less Suffix files are automatically processed in CSS modules mode.

  • /config/webpack.base.config.jsTo configure the loader:
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

//For the sake of code simplicity, I encapsulate it here
const cssTest = /\.css$/
const lessTest = /\.less$/
const cssModuleTest = /\.module\.css$/
const lessModuleTest = /\.module\.less$/
const baseCssUse = [
  MiniCssExtractPlugin.loader, 
  'css-loader', 
  'postcss-loader'
]
const baseCssModuleUse = [
  MiniCssExtractPlugin.loader, 
  {
    loader: 'css-loader',
    options: {
      modules: {
        localIdentName: "[name]_[local]__[hash:5]"
      }
    },
  }, 
  'postcss-loader'
]

module.exports = {
  module: {
    rules: [
      ......,
      //Replace the previous configuration of CSS and less with the following code
      {
        test: cssTest,
        exclude: cssModuleTest,
        use: baseCssUse
      },
      {
        test: lessTest,
        exclude: lessModuleTest,
        use: [...baseCssUse, 'less-loader']
      },
      {
        test: cssModuleTest,
        use: baseCssModuleUse
      },
      {
        test: lessModuleTest,
        use: [...baseCssModuleUse, 'less-loader']
      },
      ......,
    ]
  },
}
  • SRC directory index.module.less :
.name {
  text-decoration: line-through;
}
  • /src/index.jsAdd code:
import style from './index.module.less'

function App () {
  return (
    <div>
      The little prince

      <div className={style.name}>Neo</div>

      <div>
        <img src={icon}/>
      </div>
    </div>
  )
}
  • After configuration,npm startRestart the project, chrome console view elements and style effect.

6. Compatible processing of Polyfill

ES6 contains a new syntax and a new API. The new API is implemented in a lower level language. The new syntax can be degraded by Babel by default, but the new API will not handle it by default, such as find of array Object.assign You need to configure Polyfill to handle.
Starting from Babel V7.4, the official no longer recommends using @ Babel / Polyfill, but recommends using core JS / stable and regenerator Runtime / runtime directly.

  • Installation dependency:
npm i -S core-js regenerator-runtime
  • In the entry file/src/index.jsTop import in:
//You must import at the top of the entry file
import "core-js/stable"
import "regenerator-runtime/runtime"

//Then import the others
......
  • babel.config.jsTo modify the @ Babel / preset env configuration:
module.exports = {
  presets: [
    //Modify the configuration of @ Babel / preset env here
    [
      '@babel/preset-env',
      {
        modules: false,
        useBuiltIns: 'entry',
        corejs: {
          Version: '3.8' // your top two core JS version numbers
          proposals: true,
        },
      },
    ],
    //The rest remains the same
    ......
  ],
}
  • The above configuration method I personally recommend has the disadvantage of global namespace pollution, but the advantage is more comprehensive support. The advantages and disadvantages of other methods such as usebuiltins: usage and @ Babel / plugin transform runtime are just the opposite,Reference documents

7. Proxy

In the actual project, the local development will generally encounter the problem of cross domain interface. If any one of the protocol, domain name and port number is inconsistent, it will cross domain. Configuring proxy agent in devserver can solve the cross domain problem.

  • Before configuring the proxy, it is better to add a proxy ID before all your API request addresses, which is used for proxy matching interception and tells the proxy server which requests need to be proxied. Here, the proxy ID is tentatively set as/proxy
  • /config/webpack.dev.config.jsTo configure devserver:
module.exports = merge(common, {
    devServer: {
      contentBase: path.resolve(__dirname, '../dist'),
      open: false,
      port: 9000,
      compress: true,
      hot: true,
      inline: true,
      proxy: {
          '/proxy': {
                target: 'https://192.111:8800',
                ws: true,
                changeOrigin: true,
                secure: false,
                pathRewrite: {
                  '^/proxy': ''
                }
           }
      }
    },
})
  • targetThe target address, with port number needs to bring the port number;
  • wsConfigure whether web socket is supported;
  • changeOriginI don’t know whether the configuration supports the virtual host site;
  • secureIs security verification enabled? The target address ishttpsSet secure to false;
  • pathRewritePath rewriting, the above is to replace / proxy with an empty string after configuring the proxy, that is, the actual interface address no longer needs to carry / proxy.
  • More configuration referencehttp-proxy-middlewarefile.

8. Script variable cross env

cross-envIs a script to run cross platform settings and use environment variables. Using cross env to configure custom variables in scripts script command can realize the function of quickly switching environment configuration from command line.

For example, configure different test environments to use different interface addresses. The traditional way may be to modify the proxy address code directly in devserver. Manual modification of the code is easy to make mistakes, and code conflicts are also easy to occur in multi person development. If you use the cross env configuration variables to judge, set the corresponding proxy address, and switch the proxy by switching scripts It’s much more convenient.

  • Installation dependency:
npm i cross-env -D
  • package.jsonModify scripts in
"scripts": {
    "start": "npm run start:test1",
    "start:test1": "cross-env MY_TYPE=test1 webpack-dev-server --progress --config ./config/webpack.dev.config.js",
    "start:test2": "cross-env MY_TYPE=test2 webpack-dev-server --progress --config ./config/webpack.dev.config.js",
    "build": "webpack --config ./config/webpack.prod.config.js"
  },
  • The above configuration is setMY_TYPEFor this variable, the values set by the two commands are test1 and test2, respectivelynpm run start:test1In the webpack configuration fileprocess.env.MY_TYPEGet the value.

Cross env automatically adds the variables we set to it process.env On this object, but process.env It can only be obtained in the node environment, but not in the browser environment.

But do you remember the defineplugin described in Article 4 above? We can add a browser environment with defineplugin process.env Object in the following way:

  • /config/webpack.base.config.jsDefine the defineplugin plug-in configuration in
new webpack.DefinePlugin({
  // VERSION_H5: +new Date(),
  'process.env': Object.keys(process.env).reduce(
    (env, key) => {
      env[key] = JSON.stringify(process.env[key]);
      return env;
    }, 
    {}
  )
}),
  • And then in the/src/index.jsAdd a print statement in the
console.log(process.env.NODE_ENV)
console.log(process.env.MY_TYPE)
  • Run the command separatelynpm run start:test1andnpm run start:test2View the browser print results.

See more

Recommended Today

Third party calls wechat payment interface

Step one: preparation 1. Wechat payment interface can only be called if the developer qualification has been authenticated on wechat open platform, so the first thing is to authenticate. It’s very simple, but wechat will charge 300 yuan for audit 2. Set payment directory Login wechat payment merchant platform( pay.weixin.qq . com) — > Product […]