preface

This is a primer on webpack5 configuration from 0.

Through this article you can obtain:

  • Learn more about what WebPack does
  • Know how to package resources using WebPack
  • Know how to use WebPack to improve your development experience
  • Be able to understand configuration items in official documents
  • Can gain some knowledge of optimization

This article should take about 15 minutes to read, or longer if you can configure it based on examples.

I will try to explain to you in plain English the origin of all configuration items and what problems they solve.

Also hope the reader can match personally at the same time, deepen own impression.

Without further ado, let’s begin.

1. Basic Concepts

1.1 What is Webpack

Webpack is an open source JavaScript module packaging tool, its core function is to solve the dependency between modules, the front-end of a variety of resource files (JS, CSS, JPG, PNG, etc.) as each module in accordance with specific rules and order organized together.

It carries out static analysis according to the dependency of modules, and finally generates the corresponding static resources.

This process is called module packaging.

The build diagram on the official website is a good illustration of what Webpack does

1.2 Modules in JS

The so-called module is to divide the code of a specific function into a number of code fragments, each fragment to achieve a purpose, and finally combine them together through the interface. Each module works together to ensure the normal operation of the program.

For a long time, JavaScript, unlike other programming languages, could be developed using modularity. This is because the language was created as a lightweight scripting language that provides validation for users when uploading forms.

With the increasingly complex business and the development of front-end technology, it has gradually become normal to introduce multiple JS files into the page, which also exposes some problems:

  • You need to manually maintain the loading order of the JavaScript. There are often dependencies between multiple scripts on a page, but because these dependencies are implicit, problems can arise when there are too many JS files.
  • Each script tag means a static resource is requested once, and too many requests can slow down the rendering of the page.
  • In each script tag, the top-level scope is the global scope, which can be contaminated if variables or functions are declared directly in code without processing.

Modularization solves the above problems one by one:

  • Dependencies between modules are analyzed through import and export statements
  • Use tools to package all JS files into one or more files to reduce network overhead
  • Multiple modules are scoped separately from each other and do not have naming conflicts with each other

For modularity, the community proposed AMD, CMD, CommonJS and other schemes, ES6 module standard will be promoted to the language level modularity. However, the ES6 standard module is not available for practical application due to the following reasons:

  1. Code Splitting and tree shaking are not available
  2. Most NPM modules use ComminJS rules, whose syntax is not supported by browsers
  3. Browser compatibility issues

Therefore, we need to use the module packaging tool to help us complete a series of tasks.

The job of the module packaging tool is to resolve dependencies between modules so that the packaged results can run in the browser.

1.3 What does the packaging tool do

One advantage of using packaging tools is that they give us more control over how modules are parsed, allowing us to use bare modules and more functionality such as CSS/HTML modules.

The packaging tool does the following:

  1. From one intended to be placed in HTML<script type="module">The “main” module starts. Webpack defaults fromsrc/index.jsStart)
  2. Analyze its dependencies: its imports, the imports of its imports, and so on.
  3. Build a file (or files, if tunable) from all modules and replace the native one with a bundler functionimportCall to make it work properly. “Special” module types such as HTML/CSS modules are also supported.
  4. Other transformations and optimizations may be applied during processing:
    • Remove unreachable code.
    • Remove unused exports (” tree-shaking “).
    • Remove development-specific imagesconsoledebuggerStatement like this.
    • You can use Babel to convert cutting-edge modern JavaScript syntax into older JavaScript syntax with similar functionality.
    • Compress the generated file (remove whitespace, replace variables with short names, etc.).

If we use a packaging tool, scripts are packaged into a single file (or several files) in which import/export statements are replaced with special bundler functions. Therefore, the final packaged script does not contain any import/export, and it does not need type=”module”, we can put it in the regular

<! -- Suppose we get the "bundle.js" script from a packaging tool such as Webpack -->
<script src="bundle.js"></script>
Copy the code

1.4 Advantages of Webpack

  1. Supports various module standards by default, including AMD, CommonJS, ES6 modules, etc. It helps us deal with dependencies between modules.

  2. Complete code Splitting solution. It can split packaged resources, load only necessary parts of the first screen, and load less important functions dynamically. This helps to reduce resource volume and improve the rendering speed of the home page.

  3. Webpack can handle all types of resources, JS, images, CSS, and so on.

  4. Webpack has a huge community support and plug-ins.

  5. Webpack can improve the experience of front-end development. Webpack-dev-server has a complete set of collaborative development functions to improve the efficiency of front-end development and debugging

1.5 Five core concepts of Webpack

  • Entry: The entrance. Specifies the file from which the packaging tool builds the internal dependency diagram and uses it as a starting point for packaging

  • Output: indicates the output. Specify where the packaged bundles resources are ultimately exported, and what is the output name

  • Loader: indicates the loader. Let WebPack handle the translation and packaging of non-JS files. (Such as static resources such as less and image)

  • Plugin: plug-in. Enable WebPack to handle functional tasks such as packaging optimization, compression, and template generation.

  • Mode: indicates the mode. Development mode, Production mode, None. You can set the value of process.env.node_env and automatically enable some plug-ins depending on the environment.

1.6 summary

From the above introduction, we can get a general idea of the concept and function of WebPack.

Now let’s go to the actual combat, where we need to do some preparatory work.

Second, preparation

2.1 Initializing the Project

mkdir webpack-demo-1
cdWebpack-1 // Initialize yarn init -y // Install webpack and CLI tools YARN add webpack webpack-cli --dev // Check the version NPX webpack -vCopy the code

To test this, we first create the SRC directory in the root directory and create three files (index.html in the root directory)

Index.html SRC ├─ index.css ├─ index.htmCopy the code

Add content to each:

index.css

.div {
  color: red;
  font-size: 16px;
}
Copy the code

index.html

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <title>first webpack app</title>
  </head>
  <body></body>
  <script src="./dist/bundle.js"></script>
</html>
Copy the code

index.js

function createElement() {
  const div = document.createElement('div');
  div.innerHTML = 'hello world';
  div.classList.add('div');
  document.body.append(div);
}
createElement();
Copy the code

2.2 Packaging the first JS file

After initialization, we can see the on index.html. Currently, our project does not create dist, and bundle.js does not exist.

We expect to be able to package the contents of index.js with webpack to generate the dist directory and bundle.js file

At this point, enter:

npx webpack --entry=./src/index.js --output-filename=bundle.js --mode=development
Copy the code

NPX webpack means to start webpack locally with NPX

The –entry argument looks for the SRC /index.js file in the current directory

–output-filename specifies the output filename

— Mode refers to development mode

At this point, the dist directory should be added to the root directory, and there is a bundle.js file, which is the packaged index.js file.

If you open your local project with http-server or vscode’s live server plugin, you’ll see hello world typed at the front of your chrome browser screen, indicating that the package has been successfully packaged.

Since the current configuration starts from 0, you need to download http-server or vscode’s live-server plug-in yourself to view the packaged project page.

To summarize, what we just did is:

Webpack looks for module dependencies with the entry file SRC /index.js as the entry point. There are no other dependencies at this point. So output as bundle.js.

The final parameter mode refers to the packaging mode, and there are three modes: development, production, and None. It automatically adds a set of configurations that are appropriate for the current mode, reducing human effort.

2.3 configuration script

Since using the CLI method adds a lot of command parameters and is not easy to maintain, we need to add scripts in package.json so that we don’t need to type such long instructions.

Add the command to package.json:

."scripts": {
    "build": "webpack --output-filename=bundle.js --mode=development"},...Copy the code

The above script omits the configuration of Entry.

This is because webpack defaults to the index.js entry file from the SRC directory of the project root, and the packaged file is automatically placed in the dist directory. So we can simplify our command line by following the default directory configuration.

In this case, yarn Build can also be used to package packages.

2.4 Configuration Files

Webpack provides a number of command-line arguments that can help meet the needs of a variety of scenarios.

As we saw in the example above, we can customize the entry file and output file name, specify the mode, etc.

These command-line arguments can be obtained using the following command

NPX webpack - hCopy the code

Adding more parameters to the command is only suitable for projects with fewer configurations; if there are more configurations, we need a special configuration file.

Webpack reads this configuration file every time it is packaged, so you don’t have to add too many parameters to the command line for later maintenance.

The default configuration file is webpack.config.js, or you can specify the configuration file with the command line argument –config.

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

Create webpack.config.js in the root directory and add the configuration items as shown in the previous command line parameters:

// webpack.config.js
module.exports = {
  entry: './src/index.js'.output: { filename: 'bundle.js' },
  mode: 'development'};Copy the code

Output. filename remains the same as before, but absolute paths are required to configure output.path — the final resource output path.

The default configuration output path is equivalent to

  output: {  path: path.join(__dirname, 'dist')}Copy the code

Since it is the default configuration, the output.path configuration is omitted from webpack.config.js

With the configuration items written, we can remove the packaging parameters configured in package.json.

  "scripts": {
    "build": "webpack"
  },
Copy the code

After yarn Build is executed, Webpack prereads the configuration in webpack.config.js and packages it.

When built, use the editor to open and view the results on Chrome.

2.5 summary

The preparation is to familiarize yourself with webpack-CLI, Webpack scripts, and default items of webPack configuration files.

We also packaged a JS file with WebPack, which doesn’t even require any configuration because WebPack has the ability to package JS itself.

However, it does not have the ability to package other static resources (CSS, Sass, file files, etc.) by default, so we need to configure loader to “translate” these static resources.

Along with the translation, we can also use plugin to let WebPack do some extra work for us, such as template file generation, compression, optimization, and so on.

The next step is to configure loader and Plugin.

3. Use Loader and Plugin to package resources

3.1 packaging CSS

Although we wrote CSS in preparation, the actual packaging does not have CSS effect, because webPack did not find the CSS import statement when analyzing the dependency.

We can introduce CSS in index.js

import './index.css';
Copy the code

Loader is needed to package resources other than JS files. We install two Loaders first:

yarn add style-loader css-loader --dev
Copy the code

Style-loader is used to insert

According to the current official website, the configuration is as follows:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{test: /\.css$/i,
        use: ['style-loader'.'css-loader'].// Notice the order, webPack reads the Loader from right to left},],}};Copy the code

Webpack determines which files to look for based on regular expressions and feeds them to the specified loader. In this example, all files ending in.css will be supplied to style-loader and CSS-loader.

The module loader can be called in chain mode. Each loader in the chain will transform the resource. The chain is executed in reverse order. The first loader passes its results (converted resources) to the next loader, and so on. Finally, Webpack expects the last loader in the chain to return JavaScript.

The above sequence is csS-loader and then style-loader.

After the configuration is complete, use YARN Build and view the result on Chrome.

3.2 packaging less

Add a style.less file to SRC:

@width: 100px;
@height: 100px;
div {
  background-color: aqua;
  width: @width;
  height: @height;
  border: 1px solid red;
  user-select: none;
}
Copy the code

Then add less to index.js:

import './style.less';
Copy the code

Then install less and less-loader

yarn add less less-loader --dev
Copy the code

Configuration:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{...test: /\.less$/i,
        use: ['style-loader'.'css-loader'.'less-loader'],},],},};Copy the code

3.3 postcss

Postcss is a tool for converting styles using JavaScript. We can use it in conjunction with Autoprefixer to add more compatible prefix code to CSS to support more browser platforms.

First we add a line of CSS code to the current index. CSS:

// index.css .div { color: red; font-size: 16px; /* To see if postCSs-loader works */+ user-select: none;
}
Copy the code
yarn add postcss autoprefixer postcss-loader --dev
Copy the code

Then configure the rules in Webpack:

  module: {
    rules: [{test: /\.css$/i,
        use: [
          'style-loader'.'css-loader',
          {
            loader: 'postcss-loader'.options: {
              postcssOptions: {
                plugins: [require('autoprefixer'],},},},],},Copy the code

Once packaged, you can look at the packaged page and see that compatibility prefix code has been added

There are many other plug-ins for CSS loading that are integrated into postCSS-preset -env, such as the ability for browsers to support 8-bit colors like #12345678 and autoprefixer support.

We can also use the default plugin directly, which is equivalent to using many small loaders like Autoprefixer.

Usage:

yarn add postcss-preset-env --dev
Copy the code

Direct configuration in the options. PostcssOptions. Plugins can, in here to replace the above the require (autoprefixer), because postcss – preset – env has the function of it.

              postcssOptions: {
                plugins: ['postcss-preset-env'],}Copy the code

Dedicated PostCSS configuration

We can use less, CSS, etc to write CSS, while postCSS needs to be represented on all CSS, so we need to configure postCSS-Loader for all CSS precompilation tools, but this will add a lot of repeated configuration code.

To solve this problem, we can use the default postcss.config.js to do common configuration for postCSS.

Create postcss.config.js in the root directory as follows:

// postcss.config.js
module.exports = {
  plugins: ['postcss-preset-env']};Copy the code

Then modify the webpack.config.js configuration synchronously

// webpack.config.js
module.exports = {
...
  module: {
    rules: [{test: /\.css$/i,
        use: ['style-loader'.'css-loader'.'postcss-loader'],}, {test: /\.less$/i,
        use: ['style-loader'.'css-loader'.'postcss-loader'.'less-loader'],},],},};Copy the code

Postcss-loader: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config.js: postcss.config. Finally, csS-loader and style-loader are executed.

3.4 importLoader

Css-loader supports the import of CSS such as @import xxx. CSS.

For example, we introduced test.css this way in index.css

@import './test.css';
.div {
  color: red;
  font-size: 16px;
  /* To see if postCSs-loader works */
  user-select: none;
}
Copy the code

The contents of test.css are as follows:

body {
  background-color: antiquewhite;
  min-height: 100vh;
  user-select: none;
}
Copy the code

However, according to the current configuration, when csS-loader detects this code, the postCSs-loader has already been loaded. As a result, the test. CSS cannot be supported by PostCSS, so you need to modify the configuration of CSS-Loader.

  module: {
    rules: [{test: /\.css$/i,
        use: [
          'style-loader',
          {
            loader: 'css-loader'.options: {
              importLoaders: 1.Postcss-loader = postcss-loader = postcss-loader = postcss-loader}},'postcss-loader',].// Notice the order, webPack reads the Loader from right to left
      },
      {
        test: /\.less$/i,
        use: ['style-loader'.'css-loader'.'postcss-loader'.'less-loader'],},],},Copy the code

When the csS-loader reads a new CSS (the new CSS may not have been processed by the postCSs-loader) and configures the options.importLoaders attribute, it will find the options.importLoaders bit again. Then re-loader again in sequence.

The example above goes back one bit to find postCSS-loader. If there is other-loader before postCSs-loader and you want it to load, enter 2.

3.5 file-loader Packs images

In real development, we put images and other assets in a directory called Assets. Now create assets under the SRC directory and place an image in it.

Then add the following code to index.js

import animal from './assets/animal.jpg';
// const animal= require('./assets/animal.jpg')

function createImg() {
  const img = new Image();
  img.src = animal;
  document.body.append(img);
}
createImg();
Copy the code

And then download

yarn add file-loader --dev
Copy the code

Configuration:

module.exports = {
  module: {
    rules: [{test: /\.css$/i,
        use: [
          'style-loader',
          {
            loader: 'css-loader'.options: {
              importLoaders: 1.Postcss-loader = postcss-loader = postcss-loader = postcss-loader
              esModule: false.// You need to close the esModule}},'postcss-loader',].// Notice the order, webPack reads the Loader from right to left
      },
    / /... Omit other loaders
      {
        test: /\.(png|jpe? g|gif)$/i,
        use: [
          {
            loader: 'file-loader'.options: {
              esModule: false.// Do not convert to esModule
              name: '[name].[hash:6].[ext]'.// Follow the rules of name+ 6-bit hash+ extension
              outputPath:'image' // Output directory},},],},],},};Copy the code

If options.esmodule is not configured, require().default or just use the import xx from ‘XXX’ statement.

In addition, you need to close the esModule in csS-loader, because the code similar to the following will be replaced by the require syntax, after replacing the require syntax, you need to use. Default to access, which is not in accordance with normal development habits.

// index.css
.div {
  background: url('./assets/animal.jpg');/* would be replaced with require('./assets/animal.jpg') */
}
Copy the code

Add the above code to index.css and test it by removing the esModule: False code from csS-loader.

3.6 Url-loader Packs images

Url-loader includes the function of file-loader. In addition, it can package images and other resources as Base64, so that there will be no corresponding static resources in the dist directory after packaging. Resources will be transformed into Base64 codes and stored in the bundle.js after packaging.

The upside is that there are fewer requests for static resources, and the downside is that the larger the static resources, the longer the page takes to display.

When we develop, we convert base64 to anything less than 10KB or 20KB.

Place an image with a size larger than 20KB and an image with a size smaller than 20KB in assets respectively. Then add the larger image to index.js and the smaller image to index.css

// index.js
import dp from './assets/dp.png';// This is the bigger picture
function createImg() {
  const img = new Image();
  img.src = dp;
  document.body.append(img);
}
Copy the code
// index.css
.div {
  background: url('./assets/animal.jpg');/* This is the smaller image */
}
Copy the code

And then download

yarn add url-loader --dev
Copy the code

Change the original file-loader configuration to the following:

      {
        test: /\.(png|jpe? g|gif)$/i,
        use: [
          {
            loader: 'url-loader'.options: {
              esModule: false.// Do not convert to esModule
              name: '[name].[hash:6].[ext]'.// Follow the rules of name+ 6-bit hash+ extension
              outputPath: 'image'.// Output directory
              limit: 20 * 1024.// Limit base64 to 20KB or less}},],},Copy the code

Delete the original dist directory and repackage it. You will see that the newly generated dist directory has only one large image packed

This is because our configuration is to convert images below 20KB into base64 code, while images above 20KB are still packaged into the image directory.

3.7 Asset module Packs static resources

Webpack5 has the asset module built in, which contains the functionality of the file-loader and url-loader older modules.

If we want to asset module will all static resources in the same naming rules package to the same directory, you can in the output. The assetModuleFilename in configuration.

  output: {
    ...
    assetModuleFilename: 'asset/[name].[hash:6][ext]'.// Asset module global configuration
  },
Copy the code

Static resources include images, files, and fonts. Therefore, global configuration is not required.

If we want file-loader functionality, we can use asset/ Resource

  module: {
    rules: [{...test: /\.(png|jpe? g|gif|svg)$/i,
        type: 'asset/resource'.generator: {
          filename: 'assets/[name].[hash:6][ext]',},},],}Copy the code

If you want urL-loader functionality, use asset/inline

  module: {
    rules: [{...test: /\.(png|jpe? g|gif|svg)$/i,
        type: 'asset/inline',},],}Copy the code

If you want to mix, use asset directly

  module: {
    rules: [{...test: /\.(png|jpe? g|gif|svg)$/i,
        type: 'asset'.generator: {
          filename: 'assets/[name].[hash:6][ext]'.// Output rules
        },
        parser: {
          dataUrlCondition: {
            maxSize: 20 * 1024.DataUrl < 20KB},},},],}Copy the code

I replace the urL-loader module above with the configuration of the asset hybrid mode directly here.

Delete the original dist directory and try packing again. Since there is no plugin, you can only manually clear the original dist directory.

3.8 Delete dist before packaging using Plugin

Note: webpack5 has this functionality built in.

Set output.clean to true in webpack.config.js

 module.exports = {
   ...
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
+ clean: true,}};Copy the code

Here is the old method:

— Dividing line —

Webpack will not delete the original dist directory when packaging, but replace the content on its basis, so we need to manually delete dist directory, the following describes the plugin that automatically helps us delete dist directory first when packaging.

yarn add clean-webpack-plugin --dev
Copy the code

Use:

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const path = require('path');
module.exports = {
  entry: './src/index.js'.output: {
    path: path.join(__dirname, 'dist'),... },mode: 'development'.module: {... },plugins: [new CleanWebpackPlugin()],// Plugins are used as classes
};
Copy the code

One thing to note here is that we need to manually set output.path.

/ / clean-webpack-plugin / / output.path / / webpack.config.js / / / output.path / /

clean-webpack-plugin: options.output.path not defined. Plugin disabled...
Copy the code

3.9 Packing Fonts

Assets/Resource can also be used to package fonts with the following configuration:

  module: {
    rules: [{...test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource'.generator: {
          filename: 'fonts/[name].[hash:3][ext]'.// Output rules}}},],Copy the code

3.10 HTML – webpack – the plugin

This plug-in is used to generate index.html.

It allows you to specify an HTML template that webPack will use to generate a new index.html after each package and automatically import the packaged resources.

Ejs files are built into the plugin as templates. We can also manually specify our own templates. Here is an example of using a template created by ourselves:

In the public directory of the root directory, we create a template with the following contents:

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <link rel="icon" href="<%=BASE_URL %>favicon.ico" />
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
Copy the code

download

yarn add html-webpack-plugin --dev
Copy the code

Plug-in configuration:

const { DefinePlugin } = require('webpack');// WebPack's built-in initialization plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js'.output: {... },mode: 'development'.module: {
    rules: [...]. ,},plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'my APP'.template: './public/template.html'.// Specify the index. HTML template instead of the plug-in's built-in EJS template
    }),
    new DefinePlugin({
      BASE_URL: '". /".// You can replace <%=BASE_URL %> in the template with "./"})]};Copy the code

After yarn Build, open dist/index.html in Chrome and see that the template has imported all the packaged resources.

This saves us the hassle of manually importing resources into index.html.

At this point, we are ready to remove the index.html from the root directory.

3.11 Use of Babel

To allow the browser platform to use JSX, TS, ES6, and so on directly, you can use the Babel plug-in to transform the syntax before packaging.

download

Yarn add @babel/core --dev // Babel Yarn add @babel/preset-env --dev // Babel Specifies the set of preset plug-ins. Yarn add babel-loader --dev // Babel's webpack loaderCopy the code

In the project SRC directory, create es6.js as follows:

export const a = () = > {
  const arr = [1.2.3];
  const arr2 = [...arr];
  const [a, ...rest] = arr2;
  console.log(a);
  console.log(rest);
};
Copy the code

Then in index.js, introduce and execute:

import { a } from './es6';
a();
Copy the code

As with PostCSS, Babel contains a large number of plug-ins, and it is a good practice to create babel.config.js in a separate root directory

// babel.config.js
module.exports = {
  presets: ['@babel/preset-env']};Copy the code

Configure in webpack.config.js:

// webpack.config.js 
  module: {
    rules: [{test: /\.js$/i,
        use: ['babel-loader'],},],}Copy the code

After the build, open bundle.js and take a closer look, we can see that our code has been translated:

3.12 copy – webpack – the plugin

In real projects, sometimes we don’t want the resources to be packaged, but simply copied to the dist directory.

For example, during development, static resources to be packaged will be put into SRC /assets, and some static resources that can be used directly will be put into the public directory. Then, during build, we hope that the resources in the public directory can be directly copied to dist directory without loader processing. So it can be used directly after Posting online.

With this requirement, we can use copy-webpack-plugin to do this for us.

Installation:

yarn add copy-webpack-plugin --dev
Copy the code

Put an image or other static resource in public under the root directory. My current public directory structure looks like this:

.├ ── PNG ├─ template.htmlCopy the code

PNG is the static resource I need to copy directly to the dist directory, and template. HTML is the template.

Then change the createImage function in index.js to look like this:

function createImg() {
  const img = new Image();
  // Note that the require or import statements are not used here, because the resources copied to dist are not needed to be packaged
  img.src = './dp.png'; 
  document.body.append(img);
}
Copy the code

When we reference this static resource, we know in advance that it will be copied to the dist directory, so we can use./ instead of require or import.

In the actual development, we sometimes also can it use static resources, many framework provides this functionality, some very large static resources (e.g., maps), etc., we don’t need them packaged tools “translate” again, but a direct copy to dist directory, in this case, we can directly use. / introduction in the form of these static resources.

And then supplementary configuration

  plugins: [
  ...
    new CopyWebpackPlugin({
      patterns: [{from: 'public'.globOptions: {
            ignore: ['**/template.html'].// Ignore template.html under public},},],},Copy the code

The template. HTML currently in public is copied to the dist directory by html-webpack-plugin and read and write as an index.html template, so we’ll ignore it here.

Without a doubt, if your project has a lot of static resources that don’t need to be packaged, then using this copy approach can greatly speed up the build before going live.

3.13 compile TS

Support for TS syntax requires typescript installation and TSC –init

Yarn add typescript @types/react --dev yarn add ts-loader --dev TSC --init // Initialize the tsconfig.json fileCopy the code

Enable sourceMap and JSX syntax in the tsconfig.json file

"compilerOptions": {
  "jsx": "react"."sourceMap": true. }Copy the code

The final configuration

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{...test: /\.tsx? $/i,
        use: ['ts-loader'],},],},};Copy the code

Here’s a bit of an interlude. We’ve already used Babel in the project, and according to an introduction in the official documentation, I think we could use another loader

Note that if you’re already using babel-loader to transpile your code, you can use @babel/preset-typescript and let Babel handle both your JavaScript and TypeScript files instead of using an additional loader. Keep in mind that, contrary to ts-loader, the underlying @babel/plugin-transform-typescript plugin does not perform any type checking.

Note that if you already use babel-Loader to dump your code, you can use @babel/preset-typesctipt to let Babel process your JVAScript and typesctipt code instead of other additional loaders. Remember that unlike ts-Loader, the underlying @babel/plugin-transform-typescript plug-in does not perform any type checking.

In this case, download @babel/preset-typescript

yarn add @babel/preset-typescript --dev
Copy the code

Modify the configuration:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.tsx?$/i,
- use: ['ts-loader'],
+ use: ['babel-loader'],},],}};Copy the code

Add presets for Babel

// babel.config.js
module.exports = {
  presets: [
    '@babel/preset-env',
    '@babel/preset-react',
+ '@babel/preset-typescript',
  ],
  plugins: [['react-refresh/babel']],
};
Copy the code

3.14 Pack CSV, TSV and XML

Webpack supports JSON built-in, but if you want to import CSV, TSV, XML and other resources, you need to install a loader

yarn add csv-loader xml-loader --dev
Copy the code
// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{...test: /\.(csv|tsv)$/i,
        use: ['csv-loader'],}, {test: /\.xml$/i,
        use: ['xml-loader'],},],},};Copy the code

3.15 Distinguish production and packaging environments

How to distinguish the production and packaging environment has been described in detail on the official website. In short, it is to divide webpack.config.js into three files

  webpack-demo
  |- package.json
  |- package-lock.json
- |- webpack.config.js
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js
Copy the code

Where dev.js stores the configuration for the development environment, prod.js stores the configuration for the production environment, and common.js stores the configuration for both.

For example, common.js places entry, exit, required plug-ins, required loaders, and so on

webpack.common.js

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

 module.exports = {
   entry: {
     app: './src/index.js',},plugins: [
     new HtmlWebpackPlugin({
       title: 'Production',})],output: {
     filename: '[name].bundle.js'.path: path.resolve(__dirname, 'dist'),
     clean: true,}};Copy the code

Dev.js is used to store configuration such as devServer for development purposes. Mode must be written (you can also pass mode by script command).

 const { merge } = require('webpack-merge');
 const common = require('./webpack.common.js');

 module.exports = merge(common, {
   mode: 'development'.devtool: 'inline-source-map'.devServer: {
     static: './dist',}});Copy the code

Prod. js stores plugins and the like for production environments

 const { merge } = require('webpack-merge');
 const common = require('./webpack.common.js');

 module.exports = merge(common, {
   mode: 'production'});Copy the code

Where dev.js and prod.js merge the configuration of common.js using the merge function provided by Webpack.

Finally, set the script command in package.json to a different config.js

  "scripts": {
    "build": "webpack --config ./webpack.prod.js"."dev": "webpack serve --config ./webpack.dev.js"
  },
Copy the code

The difficulty in distinguishing between production and development environments is that each configuration item, plug-in, and loader needs to be sorted according to the environment.

3.16 summary

In this section, we used WebPack to package some resources for us, but due to space constraints, we can’t cover all static resources here.

The same goes for actual development. We couldn’t take all of these factors into account, so we had to adapt to real problems when we encountered them, looking for solutions that were available online, and configuring WebPack to help us solve those problems.

Thanks to the authors of these integrated front-end frameworks, or due to their high technical level, or derived from a large number of practical scenarios, we have a matching, mature WebPack configuration, right out of the box.

We can refer to their configuration for minor optimizations or more extensions.

In addition to webPack capability of almost all types of static resources, it is more important for front-end development that it brings a different development experience. This is a revolutionary initiative for front-end ER, which has come all the way from ancient times, and also makes front-end have the feeling of modular development.

Here are the development tools that come with WebPack.

Iv. Development tools

4.1 webpack – dev – server

We use static servers such as Webpack-dev-server to assist in development, which has the advantage of automatically monitoring file changes and previewing them without packaging.

Our current solution is to execute yarn Build package after each change and preview the packaged project page with live-server.

It has the following disadvantages:

  • All source code needs to be repackaged after each change
  • After each successful compilation, file read and write is required, which incurs high performance overhead
  • Local refresh cannot be implemented

And Webpack’s Webpack-dev-server can provide hot update, without packaging can be previewed function, perfect solution to the above problem.

First we create a script command in package.json’s script:

  "scripts": {..."dev": "webpack serve"
  },
Copy the code

Then install

yarn add -D webpack-dev-server
Copy the code

Modify the configuration file to tell dev Server where to look for the file:

 module.exports = {
  ...
+ devServer: {
+ static: './dist',
+},. };Copy the code

The above configuration tells webpack-dev-server to serve the files in dist to localhost:8080.

Serve, the resource as the server’s accessible file

Then use the command

yarn dev
Copy the code

Webpack-dev-server is ready to start

<i> [webpack-dev-server] Project is running at: <i> [webpack-dev-server] Loopback: http://localhost:8080/ < I > / webpack - dev - server On Your Network (IPv4) : http://192.168.199.229:8080/Copy the code

Then we go to http://localhost:8080/ to get a preview.

At this point, you can delete dist directory and arbitrarily modify the source code in various places, save the modification, and Webpack-dev-server will listen to the modification of the file, so it will be reloaded in real time.

Webpack-dev-server looks for webpack.config.js by default.

If your Webpack configuration file is not the default webpack.config.js, let’s say it’s called wb.config.js then package.json needs to be

"scripts": {
 "build": "webpack --config wb.config.js"."dev": "webpack serve --config wb.config.js"
},
Copy the code

The hot update function of Webpack-dev-server mainly stores data in the cache. After each startup, data is updated in the cache, which improves the development efficiency, reduces file reads and writes, and improves the performance of the static server.

To get hot updates, we need to manually turn on HMR.

4.2 HMR function

HMR is short for Hot Module replacement, which translates as hot module replacement.

It allows all types of modules to be updated at run time without a complete refresh.

The current development model is componentially developed, and when we modify one of the components, enabling HMR enables the browser to update only the components that have partially changed the source code, rather than refreshing them all.

Webpack5 already has this functionality built in, so we just need to turn it on

 module.exports = {
  ...
  devServer: {
    static: './dist',
+ hot: true,},... };Copy the code

Then we need to write the code to hot update the module in the entry file (SRC /index.js in my case)

// src/index.js /* ... Omit other code... * /+if (module.hot) {
+ module.hot.accept(['./es6.js'], function () {
+ console.log('Accepting the updated printMe module! ');
+});
+}
Copy the code

In the above code, each entry in the array is the path to the file that needs to be hot updated. When the source code of these files is modified, local updates are triggered and callback is executed.

Any code that changes to the es6.js file in my current project is only partially flushed and can print the updated printMe Module when the hot replacement is complete!

4.3 Packing the React COMPONENT JSX

Babel is still needed to package JSX code. We need to install the following plug-ins (the first three are already used in the section “Using Babel”) :

Yarn add @babel/core --dev // Babel Yarn add @babel/preset-env --dev // Babel Specifies the preset set of plug-ins. Yarn add babel-loader --dev // Babel's webpack loader yarn add @babel/preset-react --devCopy the code

And then install react

yarn add react react-dom
Copy the code

Then add the following code to index.js

import App from './App.jsx';
import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom';

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

Then create app.jsx with the following content:

import React from 'react';

const App = () = > {
  return <div>hello world</div>;
};

export default App;
Copy the code

Modify the configuration as follows:

// webpack.config.js 
  module: {
    rules: [
    ...
- {
- test: /\.js$/i,
- use: ['babel-loader'],
-},
+ {
+ test: /\.jsx? $/i,
+ use: ['babel-loader'],
+},],}Copy the code

Add @babel/preset-react to babel.config.js

module.exports = {
  presets: ['@babel/preset-env'.'@babel/preset-react']};Copy the code

4.4 React Component Hot Update

The React component provides hot updates

The installation

yarn add @pmmmwh/react-refresh-webpack-plugin react-refresh --dev
Copy the code

Add the plug-in to webpack.config.js

const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); module.exports = { entry: './src/index.js', output: { ... }, mode: 'development', module: { rules: [ ... ] , }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'my APP', template: './public/template.html', // specify index. HTML template instead of ejS template}), new DefinePlugin({BASE_URL: './"', // You can replace <%=BASE_URL %> in the template with "./"}),+ new ReactRefreshWebpackPlugin(),]};Copy the code

The Babel plugin is currently started to loaderReact components. We also need to add the plugin to babel.config.js

// babel.config.js
module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react'],
+ plugins: [['react-refresh/babel']],
};
Copy the code

Here’s a test:

Create a new hello. JSX in the SRC directory with the following contents:

import React from 'react';

const Hello = () = > {
  return (
    <section>
      <input type='text' />
    </section>
  );
};

export default Hello;
Copy the code

Then go to app.jsx and use this component

import React from 'react';
+ import Hello from './Hello.jsx';

const App = () => {
  return (
    <div>
      hello231
+ 
      
    </div>
  );
};

export default App;
Copy the code

Open localhost:8080 and type anything in the input box:

To test whether the component supports hot updates, let’s make any changes in the App component. In this case, we’ll change hello231 to Hello I am qiuyanxi.

We assume that changes to the App component will not affect the Hello component, so whatever was entered in the input field will still be there.

After saving, take a look at the page

View chrome Console prompts

[HMR] Updated modules:
[HMR]  - ./src/App.jsx
[HMR] App is up to date.
Copy the code

4.5 Vue Component Hot Update

Vue components need to be loaded by vue-loader

yarn add vue-loader --dev
Copy the code

Compared with React, Vue configuration is much simpler. The Vue component supports HMR functionality by default, so no additional configuration is required.

// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports={
		...
  module: {
    rules: [
    ...
+ {
+ test: /\.vue$/i,
+ use: ['vue-loader'],
+},
    ],
  }
  plugins: [
		...
+ new VueLoaderPlugin(),],}Copy the code

4.6 the output publicPath

At packaging time, the output.publicPath attribute affects the reference path inside the packaged index.html.

If this parameter is not set or is set to an empty string, origin+/+output.filename is used to fetch the packaged resource

Here’s an example:

My current Settings are as follows:

module.exports={
  output: {
    filename: 'bundle.js'.path: path.join(__dirname, 'dist'),
    publicPath: ' '.// The default value is an empty string}},Copy the code

So the packaged script is referred to like this

<script defer src="bundle.js"></script>
Copy the code

When we open the HTTP server to access to the local http://127.0.0.0, automatically access to http://127.0.0.0/bundle.js

The browser will automatically add a slash for us.

To be safe, we set output.publicPath to /

module.exports={
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist'),
+ publicPath: '/',}},Copy the code

The packaged script references look like this

<script defer src="/bundle.js"></script>
Copy the code

So we manually added theta.

The original version does not know whether it is a webpack bug or a browser bug, plus/may cause the built resources cannot be accessed locally, at the time of writing this blog, there is no such problem. The local static server uses http-server

4.7 Common Configuration of devServer

Devserver. hot: When HMR is turned on, we set it to true, but it is better to set it to only because the page is not refreshed as a fallback when the build fails. For example, when you write wrong code on a component, setting it to only will not automatically refresh the entire component.

Devserver. open: if this parameter is set to true, the browser is automatically opened when the server service is started

Devserver. port: sets the port number

DevServer. HistoryApiFallback: using HTML 5 History API, may have to provide the index. The HTML page to replace any 404 response.

  devServer: {
    static: './dist'.hot: 'only'.// Do not refresh the page when the build fails as a fallback
    open: true.// Automatically open the browser
    port: 8888./ / the port number
    compress: true.// Automatic compression
    historyApiFallback: true,},Copy the code

Comparison is worth said devServer historyApiFallback, let’s create a directory SRC/components, two React again in a few new components, as follows:

├── ├─ b.jsx ├─ b.jsxCopy the code

As follows:

// Home.jsx
import React from 'react';

const Home = () = > {
  return <div>Home</div>;
};

export default Home;
Copy the code
// About.jsx
import React from 'react';

const About = () = > {
  return <div>about</div>;
};

export default About;
Copy the code

Then modify app.jsx source code

import React from 'react';
import Hello from './Hello.jsx';
+ import Home from './components/Home.jsx';
+ import About from './components/About.jsx';
+ import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

const App = () => {
  return (
    <div>
      hello i am qiuyanxi
      <Hello />
+ 
      
+ 
      Home
+ 

+ About + + } /> + } /> + + </div> ); }; export default App; Copy the code

Now we enter the page http://localhost:8888/about, refresh it.

If historyApiFallback is not configured, 404 will be prompted. Because this is similar to the back-end request interface and /about is the front-end route, a 404 is reported.

If historyApiFallback is configured, the correct page appears.

4.8 the proxy Settings

During our development, we often encountered cross-domain problems when requesting back-end interfaces.

You need to use a proxy to forward the request.

The configuration is as follows:

  devServer: {
  	...
+ proxy: {
+ '/api': {
+ target: 'https://api.github.com',
+ pathRewrite: {'^/ API ': '}, // rewrite/API to null
+ changeOrigin: true, // Change the host source, generally do not need to set
+},
+},
  },
Copy the code

For example, there is a ready-made Github interface:

https://api.github.com/users
Copy the code

After I set up the above proxy locally, I accessed it through AXIos

axios.get('/api/users').then(res= > {
  console.log('res.data:', res.data);
});
Copy the code

If you don’t write pathRewrite forwards the request to https://api.github.com/api/users.

If you don’t want to pass/API, you need to rewrite the path via pathRewrite. Rewritten forwarded to https://api.github.com/users

By default, the agent retains the source of the host header.

https://api.github.com/users this interface in response to the data by judging source, we can set changeOrigin: true to circumvent making judgment. This is not typically required for development

4.9 Resolve Resolve rules

Webpack has its own set of parsing rules within it, which can also be modified through the Resolve setting.

Common are

  1. Alias: Sets an alias

        alias: {
          The '@': path.resolve(__dirname, 'src'),},Copy the code

    After the above aliases are set, you can import XXX from ‘@/xx’ to import xx files in the SRC directory under the root directory.

  2. EnforceExtension: Whether importing with an extension is allowed.

  3. Extensions: Attempts to resolve the suffix name in order. If there are multiple files with the same name but different suffixes, WebPack will parse the files with the suffixes listed at the top of the array and skip the rest.

    For example, the current configuration is as follows:

      resolve: {
        extensions: ['.js'.'.jsx'.'.tsx'],},Copy the code

    When I modify the following code:

    - import Home from './components/Home.jsx/ / modify+ import Home from './components/Home
    Copy the code

    Webpack is automatically suffixed according to the Extensions property. If the file is not found after the suffix is added, an error is reported.

  4. MainFiles: file name to be used when parsing a directory.

    Let’s say I have a components directory, and I import it somewhere like this:

    import xx from './components'
    Copy the code

    If resolve is set to mainFiles: [‘index’], the index file in the components directory is introduced.

4.10 the source map

As the name implies, the Source Map is a source code map.

If the source map is not configured, the browser error message will display the corresponding location after packaging

Let’s say I write this code in index.js

console.log(abc);
Copy the code

In the absence of a declaration, the browser will report an error

The code error being located at this point is shown in the packaged bundle.js. This is basically useless for programmers.

The programmer wants to know which line in which file in the source code is broken.

Here comes the Source Map, which locates information in source code.

Add configuration to webpack.config.js:

module.exports = {
 ...
  mode: 'development',
+ devtool: 'source-map',
}
Copy the code

After yarn Dev, you can see the error message in the source code

The idea is that, once packaged, a map mapping file is generated that represents the bundle.js mapping to the source code.

You can see it after yarn build.

Fifth, about optimization

Webpack is compiled and packaged, so you can optimize in one of two ways: faster and smaller.

How to go faster — optimize packaging speed

The speed of packaging affects the speed of hot updates during development and the speed of builds before going live.

We can optimize packaging speed in the following ways:

The mode parameter

Webpack is optimized for production or development, so we need to configure different modes for development and production environments.

Resolve configuration

Resolve allows you to manually control webPack’s lookup rules. In addition to being development-friendly, it explicitly tells WebPack to use the resolve configuration rules to find files. Proper configuration will improve WebPack’s search efficiency.

  • Alias Sets an alias.

    Aliasing through Alisa allows Webpack to look for files from top to bottom through rule items, rather than recursively.

        alias: {
          The '@': path.resolve(process.cwd(), 'src'),},Copy the code

    In addition to allowing us to reference SRC by importing xx from ‘@/ XXX ‘during development, the alias Settings above are also very friendly to WebPack’s lookup rules — WebPack knows it can find files from the SRC directory from top to bottom, Instead of looking up files recursively through relative paths.

  • Extensions Indicates the prefix of the high frequency extension

    You can set the extensions to import without writing the extension.

      resolve: {
        extensions: ['.js', '.jsx', '.tsx'],
      },
    Copy the code

    Webpack traverses the Extensions property from front to back to match if there is a file with the corresponding extension, with some high frequency suffixes in front to speed up webPack’s search

  • modulesTells WebPack which directory to search for when parsing modules

    const path = require('path');
    
    module.exports = {
      / /...
      resolve: {
        modules: [path.resolve(__dirname, 'src'), 'node_modules'],}};Copy the code

    The code above tells Webpack to search SRC and node_modules, with SRC being searched first.

    This will help speed up the search

Cache attribute

module.exports = {
  / /...
  cache: {
    type: 'filesystem',}};Copy the code

Improve build speed by setting the cache property for the file system cache of webpack modules and chunks generated.

thread-loader

Use this loader to generate separate worker pools for time-consuming operations. Each worker is a separate Node.js process.

It is equivalent to enabling multiple processes to process slow Loaders. In this way, multiple Loaders can be processed simultaneously.

Here is an example of official documentation:

module.exports = {
  module: {
    rules: [{test: /\.js$/,
        include: path.resolve('src'),
        use: [
          "thread-loader".// Time consuming loader (e.g. Babel-loader)],},],},};Copy the code

How to make it smaller – reduce the packaging volume

Ideas for reducing packaging size include using plugins to reduce code size, or using WebPack’s tree-shaking feature to remove unused code.

Compress CSS

Optimize CSS Assets Webpack Plugin

Refer to the official documentation for usage

var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
  module: {... },plugins: [
    new OptimizeCssAssetsPlugin({
      assetNameRegExp: /\.optimize\.css$/g,
      cssProcessor: require('cssnano'),
      cssProcessorPluginOptions: {
        preset: ['default', { discardComments: { removeAll: true}}],},canPrint: true}})];Copy the code

Compressed bundle

Enable compression through the Optimization property built into WebPack.

module.exports = {
  / /...
  optimization: {
    minimize: true,}};Copy the code

Tree Shaking

Tree shaking is a term used to describe removing dead-code from a JavaScript context.

This functionality is built into Webpack and is simply enabled with mode:” Production “.

The last

The Webpack ecosystem is huge and changing very quickly.

Not only ecology, plugins, configuration items, but optimizations change very quickly.

As a starting point, this blog post can only provide an idea that explains webPack as much as possible in the context of actual development.

If you’re done with this blog post, you can check out the configuration instructions in the official WebPack documentation.

Finally, we recommend ⭐️ to use vite⭐️, manual dog head

Happy New Year. See you next time 👋🏻👋🏻

Vi. Reference sources

webpack-guides

Webpack: Getting started, advancing, and tuning

To promote the

Finally, I would like to promote the Github blog that I maintain for a long time

1. From learning to summary, record important knowledge points of the front-end, including in-depth Javascript, HTTP protocol, data structure and algorithm, browser principles, ES6, front-end skills, etc.

2. There is a directory in the README to find relevant knowledge points.

If it is helpful to you, welcome star and follow.