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 to map to each module needed by the project, and then generates all those dependencies into one or more bundles. — From the official Webpack documentation

Simply put, Webpack is a packer for modern front-end development. Modern front-ends are very popular to develop in a modular way, and what Webpack does is to pack JS modules into bundles or chunks in a reasonable way. In the process of packaging, you can use the way of the loader, the new JS syntax, CSS pre-processing language, etc., into a form that is more easily supported by the browser.

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

Module. exports = {mode: 'development' // exports = {mode: 'development' // exports = {mode: 'development' // exports = {mode: 'development' // exports = {mode: 'development' [], // The corresponding plugin devServer: {}, // Development server configuration (for development use only)}

Next, let’s go through the configuration step by step.

To prepare

Initialize and install

Initialize it by executing NPM init in the specified folder.

mkdir webpackDemo&&npm init

Because the project is not a project to be published to NPM, executing NPM init is all about pressing Enter.

Install Webpack and React dependencies:

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

In later versions of Webpack4, you will also need to install Webpack-CLI, as above.

Create the initial directory structure and files

Create the config folder in the project root directory and create webpack.config.js inside.

Open package.json configuration scripts in the root directory:

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

The purpose of configuring scripts is to perform command-line input at a later stage of execution simply by typing 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 the SRC folder in the root of the project and create index.js, app.js, and index.css inside.

index.js

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


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

App.js

import React from 'react'; export default class App extends React.Component { render() { return <div> <p ClassName ="text"> </p> </div>}}

index.css

.text{
    color:'red'
}

When you have done so, the project directory structure should look like the following

│ node_modules, sigma ──config │ webpack.config.js, sigma ── SRC │ index.js │ index.css │ app.js package.json

Now that we’re done with simple initialization, let’s get to know Webpack.

Mode (mode)

Mode is a new concept in Webpack4. It has three options: development, production, and none to set Webpack optimizations.

development

Development mode, which optimizes development speed and provides detailed error mechanisms and browser debugging tools. It also turns off code compression, allowing code to build faster.

production

Production mode, which provides smaller code packages and eliminates code that is only run during development. Code obfuscation compression is automatically enabled.

configuration

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

Program Entry

Here, you can declare the starting point of an application. Entries can have one or more. In a single page application, there is usually only one entry. However, it is also possible to configure a common dependency as an entry point for a single page application, so that a single page application can have multiple entries. In a multi-page application, there are usually multiple entry files.

A simple single-page application entry looks like this:


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

Output (output)

Output is used to configure the file name and path of the packaged project. It tells Webpack how to print, where to print, 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 Node.js path module) path: path.resolve(__dirname, './.. / build '), / / output filename configuration filename: "[name] [hash] js"}}

Instead of giving it an actual name, the filename here uses the template string to set the name of the Webpack generated file. The [name] in this setting represents the module name, which defaults to main in a single-entry file. The [hash] generates a hash of the module identifier, which is 20 bits by default and can be specified using [hash:16]. After packaging file name like this. The main f236aaeca342dfb1f8dd. Js. Following the hash after the generated file name helps us when the project redeploys and the browser downloads the new file because the referenced file name changes, 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 purpose of the Loader is to handle these modules in Webpack.

There are many types of modules in Webpack. The common ones are:

  • Modular JS files
  • The CSS/less/sass files
  • Images and static files

Loader configures in the module:

// Example const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {mode: 'development', // exports: './ SRC /index.js', // exports: {pathInfo: True, // the destination path of all output files // must be the absolute path (using the Node.js path module) 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 which folder to exclude, optimize the packaging speed include: appSrc, // to specify which folder to include, optimize the packaging speed loader: "Babel-loader ", // Loader used for the specified file}]}};

To process these modules, different loaders will be used. Before we do that, let’s briefly introduce the loader we need to use.

babel-loader

Babel is a syntactic converter that allows you to freely use the latest JavaScript syntax. It converts the new syntax we write, JSX, etc., into a form that is browser-friendly.

To use babel-loader you need the following dependencies. You can install them by doing NPM install –save-dev babel-loader @babel/core @babel/preset-react @babel/preset-env.

  • babel-loader
  • @babel/core Babel core component, which contains the Babel API.
  • @babel/preset-env is used to escape JavaScript syntax.
  • @babel/preset-react is used to escape react.

Configure the Babel – loader:

const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {mode: 'development', // exports: './ SRC /index.js', // exports: {pathInfo: True, // the destination path of all output files // must be the absolute path (using the Node.js path module) 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: {// Specifies Babel pre-escape presets: ["@babel/preset-env", "@babel/preset-react"] } } ] } }

Once this is done, you need to configure Babel to convert the new syntax of React and JS. You can specify Babel preprocessing as above using the Presets field in the Option option in the Webpack configuration.

You can also create Babel’s configuration file.babelrc in the root directory of your project. The.babelrc suffix rc comes from Linux, and many files ending in rc, such as.bashrc, are short for run command, translated into Chinese as a runtime command, which means that the program will execute the call to this file.

Babel does almost everything to read the configuration file, except for some that set the options parameter in the callback function, which, without the configuration file, reads the configuration from the Babel property in the package.json file.

Add the following statements to.babelrc:

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

url-loader

URL-loader and file-loader are similar in that they enable Webpack to package static files. The url-loader is more powerful than the file-loader and can be packaged in two ways.

URL-loader has an important parameter, limit, which is used to set a limit on the size of the packaged file. It can return a file in dataURL (base64) condition when the file is smaller than the specified parameter. When the file is larger than the specified parameter, it will be packaged through file-loader.

Configure the url – loader:

const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {mode: 'development', // exports: './ SRC /index.js', // exports: {pathInfo: True, // the destination path of all output files // must be the absolute path (using the Node.js path module) 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: {// Specifies Babel pre-escape presets: [" @ Babel/preset - env ", "@ Babel/preset - react"]}}, {/ / url - loader configuration test: / \. (PNG | JPG | GIF) $/, loader: "URL-loader ", options: {// Sets maximum size limit for URL-loader to DataURL limit: 10000}}]}}

The 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

Both style-loader and css-loader are used to handle CSS files, but they do not work the same way.

CSS-Loader: It is used to read the contents of CSS files and take care of them, such as minimize.

Style-loader: Inserts CSS files that were imported into JS as imports into the

tag.

The configuration in Webpack is as follows:

const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {mode: 'development', // exports: './ SRC /index.js', // exports: {pathInfo: True, // the destination path of all output files // must be the absolute path (using the Node.js path module) 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: {// Specifies Babel pre-escape presets: ["@babel/preset-env", "@babel/preset-react"] } }, { test: /\.(png|jpg|gif)$/, loader: "url-loader", options: {// Set URL-loader to DataURL file size limit: 10000}}, // Configure CSS file for 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: // This should be set to false in development mode to optimize packaging speed}}]}]}}

As shown above, when we are configuring multiple loaders for the same type of file. You can declare the loader in an array, and the array item can be either an object or just a string, depending on whether you have specific Settings for a particular loader. For example, when configuring CSS-loader, the option is declared and the minimize option is enabled within the option. But when configuring style-loader, only a string is written.

It is important to note that the order of execution of the loader in the array is from the last item in the array forward. So we’ve configured CSS-loader in the back, and it’s executed first. It’s more logical to process CSS before inserting it into HTML.

The plug-in

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

Take the famous HtmlWebpackPlugin as an example.

Consider a scenario where you need to manually create an HTML file during packaging and then import the various packaged files into it. Even after creating the HTML file, because in the config set the hash form of the package file name. We also had to change the name of the file introduced in HTML after each packaging based on the hash name change, which was a very low-level rework.

The HtmlWebpackPlugin solves this problem for us. The HtmlWebpackPlugin automatically generates HTML files based on the templates we provide and introduces packaged content.

Here is the process of using the 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, Initial -scale=1.0"> <meta http-equiv=" x-ua-compatible "content="ie=edge"> <title>Document</title> </head> <body> <div id="root"></div> </body> </html>

Then configure the plug-in in Webpack:

const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') // Add HTML -webpack-plugin const htmlwebpackPlugin = require(' HTML -webpack-plugin'); Module. exports = {mode: 'development', // exports: './ SRC /index.js', // exports: {pathInfo: True, // the destination path of all output files // must be the absolute path (using the Node.js path module) 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: {// Specifies Babel pre-escape presets: ["@babel/preset-env", "@babel/preset-react"] } }, { test: /\.(png|jpg|gif)$/, loader: "url-loader", options: {// Set URL-loader to DataURL file size limit: 10000}}, // Configure CSS file for style-loader and css-loader {test: /\.css$/, include: Appsrc, use: ['style-loader', {loader: 'css-loader', options: {// It can contain some configuration minimize: }}]}, plugins: [// new HTMLWebpackPlugin ({file: Template: 'public/index.html' // Specify the template file})],}

Now execute NPM build from the command line, and Webpack will package the files in the SRC directory. A build file will be generated in the root directory and the packaged content will be exported there.

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. It is also very simple to change to production mode, which can be done in two ways.

  1. Change the mode option in the configuration file to change development to production.
  2. Remove the mode option in the configuration and change the build item in package.json scripts towebpack --mode production --config ./config/webpack.config.js.

In Configuration 2, you can set the packaging mode for Webpack-CLI using –mode. After the modification, the code is packaged again, which is optimized by the Webpack production pattern, obfuscated and compressed, and becomes the release version.

devServer

In the course of daily development, you certainly can’t rebuild every time you change something, and then your development efficiency is greatly affected. You need to start a service that listens for changes to the file. It repacks the file when it’s saved, and it refreshes the browser automatically so we can see updates in real time.

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

Go to NPM install –save-dev webpack-dev-server to install the plugin and add the config item devServer in Module. explot.

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

  • ContentBase: “, which tells the server which directory to serve the content from
  • HTTPS: true | false, whether to enable HTTPS
  • Compress: true | false, whether to enable compression
  • Host: ‘127.0.0.1’, specifying the host address
  • Port: 23333, specify the 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, '.. / SRC ') // Add HTML -webpack-plugin const htmlwebpackPlugin = require(' HTML -webpack-plugin'); Module. exports = {// exports entry: './ SRC /index.js', // exports output: {pathInfo: True, // the destination path of all output files // must be the absolute path (using the Node.js path module) 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: {// Specifies Babel pre-escape presets: ["@babel/preset-env", "@babel/preset-react"] } }, { test: /\.(png|jpg|gif)$/, loader: "url-loader", options: {// Set URL-loader to DataURL file size limit: 10000}}, // Configure CSS file for style-loader and css-loader {test: /\.css$/, include: Appsrc, use: ['style-loader', {loader: 'css-loader', options: {// It can contain some configuration minimize: }}]}, devServer: {// HOST HOST: '127.0.0.1', // port: // display progress: true,}, plugins: [// new HtmlWebpackPlugin({file: 'index.html', // generate file name template: 'public/index.html' // specify template file})]}

Note that devServer should be used in a development environment, so now you need to change the previous configuration.

  1. Remove the mode item in the configuration.
  2. Add another startup command to the scripts for package.json"start": "webpack-dev-server --open --mode development --config ./config/webpack.config.js"
  3. Change the previous build item towebpack --mode production --config ./config/webpack.config.js.

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

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

The advanced

In the configuration above, we have implemented a basic configuration for the React project development environment. But this is far from enough. In a real project, there are many tools that can be used to optimize development speed. At the same time, it also needs to write different configurations and do different optimizations for different environments. Also, there may be configuration involved in code splitting, compression, and so on.

Now, let’s step through the Webpack configuration.

devtool

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

To learn more about the Source Map, check out this article. Simply put, the Source Map is the file that helps us locate the error message. Proper configuration of Source Map can improve development efficiency and locate errors faster.

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

In a development environment, cheap-module-eval-source-map is preferred, as it can help to accurately locate the incorrect source code and also provide faster build speed and performance.

In a production environment, you can use source-map without starting any source map (devtool is not configured) or source-map. It is important to note that the Source Map is not deployed to a production server.

Configure the loader for the SVG file

Typically, projects need ICONS. There are many common ways to use ICONS, such as Sprite, font ICONS, SVG, etc. The way Sprite and Iconfont are used doesn’t require any special treatment, so we won’t go into it here. Here’s a way to use SVG ICONS.

With SVGR, you can directly introduce SVG ICONS into your project as React components.

It’s 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>
    }
}

In the latest version of the CLI Create-React app, SVGR has been integrated by default. It’s easy to use in our own projects, just add a loader to.svg.

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

SVGR also supports node, react-native and other processing methods, which can be learned from the SVGR documentation.

Build configurations for different environments

The build goals are very different in production and development environments. For example, in a development environment, you need faster builds and stronger error messages. But in a production environment, you want to build code that is smaller, lighter, and more performance-focused. Therefore, different configuration files are required for different environments. But if you split the configuration completely, you might have a lot of duplicate code in the two configuration files. At this point we need to come up with common configurations, and in order to merge them together, we can use webpack-merge.

Next, let’s start using Webpack-Merge for configuration optimization.

First, the NPM installation relies on NPM install –save-dev webpack-merge

Then, create webpack.config.mon.js, webpack.config.dev.js, webpack.config.prod.js in the config folder. As the name suggests, these three configurations represent configuration files for generic, development, production modes.

Expose the public configuration used in the previous configuration to webpack.config.mon.js:

// Pack HTML const HtmlWebpackPlugin = require('html-webpack-plugin'); const path = require('path'); const appSrc = path.resolve(__dirname, '.. / SRC ') module.exports = {// exports: './ SRC /index.js', module: {rules: Test: /\.svg$/, include: appSrc, use: ['@svgr/webpack']}]}, plugins: [// new HtmlWebpackPlugin({file: 'index.html', template: 'public/index.html'})]}

Configuration in 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', // exports output: {pathInfo: true, // The destination path for all output files // must be an absolute path (using the Node.js path module) // '[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: 23333, // Error message on the web mask overlay: true, // Progress: true,}})

Production environment configuration file:

const path = require('path'); const merge = require('webpack-merge'); const common = require('./webpack.config.common.js'); Const cleanWebPackPlugin = require('clean-webpack-plugin'); // CleanWebPackPlugin = require('clean-webpack-plugin'); const appSrc = path.resolve(__dirname,'.. / SRC ') module.exports = merge(common, {mode: 'production', // output: {pathInfo: false, chunkFileName: 'js/[name].chunk.js', // The destination path for all output files // must be an absolute path (using the Node.js 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: [// CleanWebPackPlugin (['build'], path.resolve(__dirname, '../'))]});

Now that the configuration changes are complete, we need to modify package.json to have the launch command refer to different configuration files.

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 use the NPM start command to start the project and run the webpack.config.dev.js file, which is the development configuration file, where we can do 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 execute the build command to package a file, the build directory is generated at the root of the project and the package file is generated there. When you perform multiple builds, you may find that there are multiple versions of the packaged file in the build directory due to different hash values of the project names. To solve this problem, you can use the plug-in clean-webpack-plugin.

First install the plugin 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, '../'))
  ]
}

After configuring the plug-in, execute the NPM build command. You will notice that each time before packaging, the build directory is deleted and then recreated.

Note that this plug-in is only for production environment configuration.

conclusion

To this point, we have implemented the basic configuration of Webpack, as well as the literacy of various concepts. This is really just basic usage, and there is much more to a truly complete Webpack configuration.

Once you’ve mastered the basic configuration above, you can try some more in-depth learning, such as optimization, tree shaking, build speed optimization in production, etc.