Introduction to the

What is Webpack?

In essence, Webpack is a static Module bundler for modern JavaScript applications. As WebPack processes the application, it internally creates a Dependency graph that maps to each module required by the project, and then generates all those dependencies into one or more bundles. — From the official Webpack documentation

To put it simply, Webpack is a modern front-end development baler. Modular development is very popular in modern front-end. What Webpack does is to reasonably pack each JS module into bundles or chunks. In the process of packaging, you can use loader to convert the new JS syntax and CSS preprocessing language into a form that is more easily supported by the browser.

Webpack is based on NodeJS, and for the most part, you need to write a configuration file for it when you use it. The main structure of this configuration file is as follows:

Module. exports = {mode: 'development', // export: module: {}, // export: module: {}, // export: module: {}, // [], // Corresponding plug-in devServer: {}, // development server configuration (only used in development)}Copy the code

Next, we will complete the configuration step by step.

To prepare

Initialization and installation

Execute NPM init in the specified folder for initialization.

mkdir webpackDemo&&npm init

Since the project is not a project to publish to NPM, NPM init is simply a press enter.

Install webPack and React dependencies:

npm install --save-dev webpack react react-dom

In versions after WebPack 4, you also need to install WebPack-CLI, as above.

Create the initial directory structure and files

Create a config folder in the project root directory and create webpack.config.js within it.

Open package.json configuration scripts in the root directory:

"scripts": {
    "build": "webpack --mode production --config ./config/webpack.config.js",
  }
Copy the code

To configure scripts, you only need to enter NPM ‘Specify configuration in script’ on the command line. Such as input NPM build, it will automatically perform “webpack – mode production — config. / config/webpack config. Js” a long series of operations.

Create code folder and react entry file:

Create a SRC folder in the project root directory and create index.js, app. js, and index.css within it.

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css'


ReactDOM.render(
  <App />,
  document.getElementById('root')
);
Copy the code

App.js

import React from 'react'; export default class App extends React.Component { render() { return <div> <p </p> </div>}}Copy the code

index.css

.text{
    color:'red'
}
Copy the code

After that, the project directory structure should look like this

├ ─ ├ ─ SRC │ index.js │ index.js │ ├.js packageCopy the code

Now that we’re done with the simple initialization, it’s time to get to know WebPack.

Mode (mode)

Mode is a new concept in Webpack 4 and has three options: development, Production, and None to set how webpack should be optimized.

development

Development mode, which optimizes development speed, provides detailed error mechanisms and browser debugging tools. Code compression is also turned off, making code faster to build.

production

Production mode, which provides a smaller code package, eliminating code that only runs during development. Code obfuscation compression is automatically turned on.

configuration

module.export = {
  mode:'production' // 'development'||'production'||'none'
}
Copy the code

Program entry

Here, you can declare the starting point of an application. There can be one or more entrances. In a single page application, there is usually only one entry. However, common dependencies can also be configured as entry points for single-page applications, which can also have multiple entries. In multi-page applications, there are usually multiple entry files.

A simple single-page application entry is as follows:


module.export = {
  mode:'production' // 'development'||'production'||'none',
  entry:'./src/index.js',
}

Copy the code

Output (output)

Output is used to set the file name and path of the packaged project. Used to tell WebPack how to output, where to output, and what to call it.

const path = require('path'); module.export = { mode:'production' // 'development'||'production'||'none', entry:'./src/index.js', output: Note: This option should not enable pathinfo:true in production mode, // The destination path of all output files // must be an absolute path (using the Path module of Node.js) path: path.resolve(__dirname, './.. / build '), / / output filename configuration filename: "[name] [hash] js"}}Copy the code

Filename here does not give it an actual name, but instead uses the template string to set the name of the file generated by WebPack. [name] in this setting represents the module name, which defaults to main in single-entry files. [hash], on the other hand, produces a hash of the module identifier, which defaults to 20 bits and can be specified using [hash:16]. After packaging file name like this. The main f236aaeca342dfb1f8dd. Js. Following the hash after generating the file name helps when the project is redeployed and the referenced file name changes, the browser will download the new file instead of continuing to use the local cache.

loader

The purpose of Webpack is to process and package the various modules in front-end development. The loader’s job is to process these modules in the Webpack.

There are many types of modules in Webpack, common ones are:

  • Modular JS files

  • The CSS/less/sass files

  • Images and static files

Configure loader in module:

// example const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {mode: 'development', // entry: './ SRC /index.js', // export: {pathinfo: Path: path.resolve(__dirname, './.. / build '), / / output filename configuration filename: "bundle. Js"}, the module: {rules: [{test: / \. (js | JSX) $/, / / used for the specified file type Support for regular exclude: /node_modules/, // to specify the folder to exclude, optimize the packaging speed include: appSrc, // specify the included folder, optimize the packaging speed loader: "Babel-loader ", // Loader for specified file}]}};Copy the code

To process these modules, different Loaders are used. Before this, the loader that needs to be used is briefly introduced.

babel-loader

Babel is a syntax converter that allows you to freely use the latest JavaScript syntax. It can translate the new syntax, JSX, and so on that we’ve written into a form that browsers can support in a friendly way.

The following dependencies are required to use babel-loader, which can be installed by running NPM install –save-dev babel-loader @babel/ core@babel /preset-react @babel/preset-env.

  • babel-loader

  • @babel/core

    The core component of Babel, which contains the Babel API.

  • @babel/preset-env

    Used to escape JavaScript syntax.

  • @babel/preset-react

    Used to escape react.

Configure the Babel – loader:

const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {mode: 'development', // entry: './ SRC /index.js', // export: {pathinfo: Path: path.resolve(__dirname, './.. / build '), / / output filename configuration filename: "bundle. Js"}, the module: {rules: [{test: / \. (js | JSX) $/, exclude: /node_modules/, include: appSrc, loader: "babel-loader", options: {// specify presets: ["@babel/preset-env", "@babel/preset-react"] } } ] } }Copy the code

After configuring this, you need to configure Babel to convert react and JS to the new syntax. You can specify the way Babel preprocessing is done using the Presets field in the Option option in the WebPack configuration as above.

You can also create a configuration file for Babel in the root directory of your project. Bashrc. Rc is an abbreviation of run command, which translates to “run command” in Chinese, indicating that this file will be called when the program executes.

Almost all Babel operations are read from this configuration file, except for those that set the options parameter in the callback function. If there is no configuration file, the configuration is read from the Babel property of the package.json file.

Add the following statement to.babelrc:

{
    "presets": ["@babel/preset-env","@babel/preset-react"]
}
Copy the code

url-loader

Url-loader and file-loader are similar in that they enable WebPack to package static files. Url-loader is more powerful than file-loader. It can be packaged in two ways.

Url-loader has an important parameter limit, which sets the size limit of the packed file. It can return a DataURL (Base64) situation file when the file is smaller than the specified parameter. When the file is larger than the specified parameter, it is packaged by file-loader.

Configure the url – loader:

const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {mode: 'development', // entry: './ SRC /index.js', // export: {pathinfo: Path: path.resolve(__dirname, './.. / build '), / / output filename configuration filename: "bundle. Js"}, the module: {rules: [{test: / \. (js | JSX) $/, exclude: /node_modules/, include: appSrc, loader: "babel-loader", options: {// specify presets: [" @ Babel/preset - env ", "@ Babel/preset - react"]}}, {/ / url - loader configuration test: / \. (PNG | JPG | GIF) $/, loader: "Url-loader ", options: {// Set the file size limit for url-loader to DataURL limit: 10000}}]}}Copy the code

Url-loader also has two parameters, mimeType and fallback, which are not used much and will not be described here.

Style – loader and CSS – loader

Style-loader and CSS-loader are both used to process CSS files, but they do different things.

Css-loader: used to read and process the contents of CSS files, such as minimize.

Style-loader: Inserts the CSS file imported to js through import into the

tag.

The configuration in Webpack is as follows:

const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {mode: 'development', // entry: './ SRC /index.js', // export: {pathinfo: Path: path.resolve(__dirname, './.. / build '), / / output filename configuration filename: "bundle. Js"}, the module: {rules: [{test: / \. (js | JSX) $/, exclude: /node_modules/, include: appSrc, loader: "babel-loader", options: {// specify presets: ["@babel/preset-env", "@babel/preset-react"] } }, { test: /\.(png|jpg|gif)$/, loader: "url-loader", options: {// Set the upper limit of file size for url-loader to DataURL limit: 10000}}, // Configure style-loader and csS-loader {test: /\. CSS $/, include: appSrc, use: [ 'style-loader', { loader: 'css-loader', options: {/ / can contain some configuration modules: true | false, / / CSS whether open modular, open after the introduction of the CSS file only valid for the current page will not effect to minimize global: True // In development mode should be set to False to optimize packaging speed}}]}]}}Copy the code

As shown above, when we are configuring multiple Loaders for the same type of file. Loaders can be declared in an array. The array entry can be an object or just a string, depending on whether you have special Settings for a particular Loader. For example, when configuring CSS-Loader, you also declare the option option and enable the minimize option in the option option. However, when configuring style-loader, only one string is written.

Note that the order of execution of the loader in an array is from the last item in the array forward. So we configured csS-loader later, which is executed first. It is more logical to process CSS first and then insert it into HTML.

The plug-in

Plug-in is an extremely important function of Webpack, Webpack provides a rich plug-in interface, so that developers can freely develop plug-ins to expand the function of Webpack.

Let’s take the well-known HtmlWebpackPlugin as an example.

Imagine a scenario where, while packaging, you need to manually create an HTML file and then import the packaged files into it. Even after the HTML file is created, the package file name is set in the hash format in config. We also need to change the name of the file introduced in the HTML after each package based on the hash name change, which is very low-level rework.

The HtmlWebpackPlugin solved this problem for us. HtmlWebpackPlugin can automatically generate HTML files based on the templates we provide and import the packaged content.

The following is an introduction to the use of HtmlWebpackPlugin.

NPM install –save-dev html-webpack-plugin

After the installation is complete, create a folder public in the root directory of the project and create a template file index.html in it.

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta HTTP-equiv =" x-UA-compatible "content=" IE =edge"> <title>Document</title> </head> <body> <div id="root"></div> </body> </html>Copy the code

Then configure the plug-in in WebPack:

const path = require('path'); const appSrc = path.resolve(__dirname, '.. // const HtmlWebpackPlugin = require('html-webpack-plugin'); Module. exports = {mode: 'development', // entry: './ SRC /index.js', // export output: {pathinfo: Path: path.resolve(__dirname, './.. / build '), / / output filename configuration filename: "bundle. Js"}, the module: {rules: [{test: / \. (js | JSX) $/, exclude: /node_modules/, include: appSrc, loader: "babel-loader", options: {// specify presets: ["@babel/preset-env", "@babel/preset-react"] } }, { test: /\.(png|jpg|gif)$/, loader: "url-loader", options: {// Set the upper limit of file size for url-loader to DataURL limit: 10000}}, // Configure style-loader and csS-loader {test: /\. CSS $/, include: AppSrc, use: ['style-loader', {loader: 'css-loader', options: {// Can contain some configuration can minimize: }}]}, plugins: [// HTML template file processing plugin new HtmlWebpackPlugin({file: Template: 'public/index.html' // specify template file})],}Copy the code

Now run NPM build on the command line and webpack will package the files in the SRC directory. A build file will be generated in the root directory to output the packaged content.

At this point, we’ve actually completed the basic configuration of WebPack. However, the current configuration is packaged in development mode without compression, which is clearly not a releasable version. Changing to production mode is actually quite simple and can be done in two ways.

  1. Modify the mode option in the configuration file to change development to production.

  2. Delete configuration mode of the options that modify the package. The json scripts for webpack build – mode of production — config. / config/webpack. Config. Js.

In Configuration 2, use –mode to set the packaging mode for Webpack-CLI. After modification, the code is packaged again. At this time, the code is optimized by WebPack Production mode, obfuscated and compressed, and becomes a release version.

devServer

In the daily development process, it is definitely not possible to re-build every time you change something, so that the development efficiency will be greatly affected. You need to start a service to listen for file changes. Repackage the files as they are saved and refresh the browser automatically so we can see the updates in time.

There are several ways to do this, of which only one is described here, using the Webpack-dev-server plug-in.

Run NPM install –save-dev webpack-dev-server to install the plug-in and add the configuration item devServer in module.explot.

There are many configuration items for devServer. Here are some common configuration items:

  • ContentBase: “, which tells the server from which directory to provide content

  • HTTPS: true | false, whether to enable HTTPS

  • Compress: true | false, whether to enable compression

  • Host: ‘127.0.0.1’, specify the host address

  • Port: 23333, specified port

  • Overlay: true | false, when a compiler error or warning, in the browser display full screen layer.

  • Progress: true | false, the running schedule output to the console.

Add devServer to the configuration:

const path = require('path'); const appSrc = path.resolve(__dirname, '.. // const HtmlWebpackPlugin = require('html-webpack-plugin'); Module.exports = {// export entry: './ SRC /index.js', // export output: {pathinfo: Path: path.resolve(__dirname, './.. / build '), / / output filename configuration filename: "bundle. Js"}, the module: {rules: [{test: / \. (js | JSX) $/, exclude: /node_modules/, include: appSrc, loader: "babel-loader", options: {// specify presets: ["@babel/preset-env", "@babel/preset-react"] } }, { test: /\.(png|jpg|gif)$/, loader: "url-loader", options: {// Set the upper limit of file size for url-loader to DataURL limit: 10000}}, // Configure style-loader and csS-loader {test: /\. CSS $/, include: AppSrc, use: ['style-loader', {loader: 'css-loader', options: {// Can contain some configuration can minimize: }}]}, devServer: {// HOST HOST: '127.0.0.1', // Overlay: true, // display progress: true,}, plugins: New HtmlWebpackPlugin({file: 'index.html', template: 'public/index.html' // specify template file})]}Copy the code

Note that devServer is intended to be used in a development environment, so you now need to modify the previous configuration.

  1. Delete the mode item from the configuration.

  2. Add another startup command “start” to the scripts of package.json: “webpack-dev-server –open –mode development –config ./config/webpack.config.js”

  3. Paragraph will be build before webpack – mode production — config. / config/webpack. Config. Js.

Now, execute NPM build and webpack will be packaged using production mode. When NPM start is executed, it is packaged using development mode, and webpack-dev-server starts a service that listens for file changes.

Now run NPM start and you’re ready to start development!

The advanced

In the configuration above, we have implemented the basic configuration of a React project development environment. However, this is not enough, and in real projects, many tools may be used to optimize development speed. At the same time, different configurations and optimizations need to be written for different environments. Also, it may involve configuration such as code splitting and compression.

Let’s complete the WebPack configuration step by step.

devtool

The devtool option in Webpack controls whether and how the Source map is generated.

To learn more about source Map, take a look at this article. Simply put, a source map is a file that helps us locate error messages. Correctly configuring source Map can improve development efficiency and locate errors faster.

There are a number of configurations available for Devtool in Webpack, which you can learn about here.

In the development environment, it is recommended to use cheap-module-eval-source-map, which can help us accurately locate the source code error, but also provide faster build speed and build performance.

In a production environment, you can either start no source map (without the devtool item configured) or use source-map. Be careful not to deploy the Source Map to a production server.

Configure loader for SVG files

In general, projects need ICONS. There are many common ways to use ICONS, such as Sprite, font ICONS, SVG, etc. The use of Sprite images and iconfont does not require special treatment, so we will not go into details here. Here is a way to use SVG ICONS.

SVGR enables you to introduce SVG ICONS directly into your project in the form of react components.

Something like this:

import React from 'react';
import { ReactComponent as Icon } from './icon.svg';

export default class App extends React.Component {
    render() {
        return <div>
            <Icon width={10} height={10} />
        </div>
    }
}
Copy the code

In the latest version of the REACT CLI, create-React -app integrates SVGR by default. It’s easy to use in our own projects, just add a loader for.svg.

{
  test: /\.svg$/,
  use: ['@svgr/webpack'],
}
Copy the code

SVGR also supports node and React-native processing modes. You can refer to SVGR documents for details.

Build configurations for different environments

The build goals in production and development environments are very different. In a development environment, for example, you need faster builds and stronger error prompts. But in a production environment, you want to build code that is smaller, lighter, and more performance-oriented. Therefore, different configuration files are required for different environments. However, if the configuration is completely separated, the two configuration files may contain a lot of duplicate code. At this point we need to come up with common configurations, and to merge these configurations together, we can use Webpack-Merge.

Next, let’s start with configuration optimization using WebPack-Merge.

First, use NPM install to rely on NPM install –save-dev webpack-merge

Then, create webpack.config.common.js, webpack.config.dev.js, webpack.config.prod.js in the config folder. As the name implies, these three configurations represent profiles for the common, development, and production patterns.

Add the common configuration used in the previous configuration to webpack.config.common.js:

// webpack.config.common.js // package HTML file const HtmlWebpackPlugin = require('html-webpack-plugin'); const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {// entry: './ SRC /index.js', module: {rules: SVG $/, include: appSrc, use: ['@svgr/webpack']}]}, plugins: New HtmlWebpackPlugin({file: 'index.html', template: 'public/index.html'})]}Copy the code

Configuration in the development environment:

const merge = require('webpack-merge'); / / into public profile const common = the require (". / webpack.config.com mon. Js'); const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = merge(common, {mode: 'development', devtool: 'cheap-module-eval-source-map', {pathinfo: true, // The destination path of all output files // must be an absolute path (using node.js's path module) // chunkFilename is configured: '[name]. The chunk. Js', / / output filename configuration filename: "bundle. Js"}, the module: {rules: [{test: / \. (js | JSX) $/, / / exclude: /node_modules/, include: appSrc, loader: "babel-loader", options: { presets: [" @ Babel/preset - env ", "@ Babel/preset - react"]}}, / / for a static file {test: / \. (PNG | JPG | GIF) $/, loader: "url - loader", the options: { limit: 8192, name: 'static/[name].[hash:8].[ext]', } }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { minimize: false } } ] } ] }, devServer: {// HOST HOST: '127.0.0.1', // port port: 23333, // overlay: true, // display progress: true,}})Copy the code

Production environment profile:

const path = require('path'); const merge = require('webpack-merge'); const common = require('./webpack.config.common.js'); // Const CleanWebpackPlugin = require('clean-webpack-plugin'); const appSrc = path.resolve(__dirname,'.. / SRC ') module.exports = merge(common, {mode: 'production', // export output: {pathinfo: false, chunkFilename: 'js/[name].chunk.js', // The destination path of all output files // must be an absolute path (using node.js's path module) Path: path.resolve(__dirname, './.. /build'), filename: "js/[name].[chunkhash:8].js" }, module: { rules: [ { test: /\.(js|jsx)$/, include: appSrc, // exclude: /node_modules/, loader: "babel-loader", options: { presets: [" @ Babel/preset - env ", "@ Babel/preset - react"]}}, / / for a static file {test: / \. (PNG | JPG | GIF) $/, loader: "url - loader", the options: { limit: 10000, name: 'static/[name].[hash:8].[ext]', } }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { minimize: true } } ] } ] }, plugins: New CleanWebpackPlugin(['build'], path.resolve(__dirname, '../'))]});Copy the code

Now that the configuration has been modified, we need to modify package.json so that the startup command references a different configuration file.

The development mode of startup configuration changes for the “start” : “webpack dev – server – the open mode development — config. / config/webpack config. Dev. Js”.

Production pattern of the launch configuration is modified to “build” : “webpack – mode production — config. / config/webpack config. Prod. Js”,

Now we start the project with the NPM start command, running the webpack.config.dev.js file, which is the development configuration file, where we can make some optimizations for the development mode.

Start the project with the NPM build command and run the webpack.config.prod.js file, which is the production configuration file where we can do some optimizations for production mode.

Prevent duplicate packaging files

When you run the build command to package files, the build directory is generated in the root directory of the project and the package files are generated in it. After multiple builds are executed, multiple versions of the packaged files may exist in the build directory due to different hash values of project names. To solve this problem, you can use the clean-webpack-plugin.

Install NPM I clean-webpack-plugin –save-dev

The configuration is as follows:

const CleanWebpackPlugin = require('clean-webpack-plugin')

// webpack config
{
  plugins: [
    new CleanWebpackPlugin(['build'], path.resolve(__dirname, '../'))
  ]
}
Copy the code

After configuring the plug-in, run the NPM build command. You’ll find that the build directory is deleted and then recreated before each package.

Note that this plug-in is only used for production configurations.

conclusion

At this point, we have implemented the basic configuration of WebPack, as well as the literacy of various concepts. This is only a basic usage, and there is certainly much more to achieving a truly complete WebPack configuration.

Once you’ve mastered the basics, you can learn more about optimization, Tree shaking, build speed optimization in production, and more.