There are tons of great Webpack articles on the Nuggets platform alone, but why should I write another one? Because too many articles are not suitable for beginners to learn, and can even lead them to misunderstand WebPack and some plug-ins, I decided to write an article that would make beginners not afraid of WebPack

Preface, you must know the knowledge is very important, be sure to read carefully!

preface

First of all, webPack is a very simple tool. The root cause of webpack’s frustration for many people is that they are unfamiliar with some of the concepts and are afraid of webPack’s rich loaders and plugins. Once you understand the concept, you will automatically know what loader and plugin you need.

You’ll be like, “That’s it? Is webpack here?” .

Personal advice: Create-react-app, for example, is a great scaffold. You may not be able to write one, but you must be able to read it and have the ability to modify the source code if you need to. This level of webpack mastery is often sufficient. In the end, I will add how to write a loader and plugin (but I believe that you will already know how to write the loader and plugin without getting to the end).

Prepare the environment and version

Node: ^ 12.16.1

Webpack: ^ 4.44.2

Webpack – cli: ^ 3.3.12

Webpackage 5 has been updated with some new features, so this article will use the current version of WebPackage 4. There is no need to worry about how to learn 4 even after 5. The principle is the same, 4 can be learned, 5 is not many new features.

The knowledge you need to know

Webpack concept

Webpack is a static module wrapper for modern JavaScript applications. If you’ve been around JS long enough, you know that there was no such packaging tool in the original project. A file is a module, and eventually these files need to be introduced into HTML using script tags in a certain order (because the wrong order can lead to lost dependency variables and other errors). But this writing project is cumbersome and inelegant. With node.js comes packaging tools (gulp, Grunt, and now most popular webpack) that run on top of Node.js, because node.js has the ability to manipulate files. So the essence of Webpack is to package JS references for us, and we often hear that various loaders, plugins, hot updates, hot module replacement and so on are a sublimation of Webpack, making Webpack can provide more help for us.

  • Loader: Webpack itself can only package JS and JSON files, but in the actual project we also know CSS, SCSS, PNG, TS and other files, then we need to use loader to make it correctly packaged.

    • Summary: Loader is used to compile files other than JS and JSON
    • Common loader:
    style-loader
    css-loader
    sass-loader
    ts-loader
    file-loader
    babel-loader
    postcss-loader
    ...
    Copy the code
  • Plugin: Plugin can do things for you when webpack runs to a certain stage, similar to the life cycle in React/Vue. A specific plugin is to inject extension logic at a specific time during the Webpack build process to change the build result, acting on the entire build process.

Make sense of these things

  • postcssPostcss is a tool for converting CSS code using JavaScript tools and plug-ins. You can think of it as Babel. It doesn’t do much on its own. Many of our specific needs need to be implemented by its plug-ins. For example, in a project where we need WebPack to automatically prefix CSS styles with compatibility, the plugin that actually prefixes them isautoprefixerBut he can prefix us only if we have postCSS.Don’t mistake PostCSS for SCSS or less
    • With the term “PostCSS,” we can refer instead to two things:
      1. The tool itself isPostCSS(What you get at run time)npm install postcss, as well as
      2. The tool supports the PostCSS plug-in ecosystem
  • babelBabel is a JavaScript compiler that allows us to code with the next generation of JavaScript syntax regardless of compatibility. But for concrete syntax conversions, we’ll use itThe plug-inTo achieve
    • After Babel7, presets are provided for us, so that we don’t have to make our own trouble to combine plug-ins. We can write whatever presets we want to run in any environment.
  • Es6 + syntax: Babel converts syntax by default, such as let, const, () => {}, class
  • Es6 + features: Babel does not convert features, which require JS code shims to be compatible with older browsers. Examples are Iterator, Generator, Set, Map, Proxy, Reflect, Symbol, Promise
  • @babel/core: @babel/core is the core library of Babel. All the core apis are in this library. These apis can be called by babel-loader
  • @babel/preset-env: This is a preset set of plugins that contains a set of related plugins, and Bable is used for guidance on code-switching. This plug-in contains all the es6 to ES5 translation rules
  • @ Babel/polyfill: @babel/preset-env just provides rules for syntactic conversion, but it can’t compensate for new features that are missing in browsers, such as some built-in methods and objects, such as Promise, array. from, etc. Polyfill is needed to be used as gasket for JS. Make up for the lack of these new features in older browsers. Note: Babel 7.4.0 this package will be deprecated
  • Core-js: It is a polyfill of the JavaScript standard library, and it can be loaded on demand. Core-js version and core-JS introduction mode can be configured using @babel/preset-env. Note: @babel/polyfill relies on core-js
  • Regenerator-runtime: provides the transcoding of generator functions

Add knowledge points (important)

  • Browserslist: Browserslist essentially declares a collection of browsers. Our tool can export compatibility code based on this list. Browserslist is used in tools like Babel, PostCSS, etc.

    • Browserslist can be configured in package.json or as a separate. Browserslistrc file

    • The tool will automatically look for the configuration in.browserslistrc, and if it doesn’t find a.browserslistrc file, it will look in package.json

    • Example:

      // in.browserslistrc
      > 1%
      last 2 versions
      
      // You can also configure rules for different environments (in.browserslistrc)
      [production]
      > 1%
      ie 10
      
      [development]
      last 1 chrome version
      last 1 firefox version
      
      // In package.json
      {
        "browserslist": ["1%" >."last 2 versions"]}// You can also configure rules for different environments (in package.json)
      Production and development depend on the configuration of the mode field in your webpack
      {
       	"browserslist": {
      		"production": [
      			"0.2%" >."not dead"."not op_mini all"]."development": [
      			"last 1 chrome version"."last 1 firefox version"."last 1 safari version"]}}Copy the code

    “> 1%” means compatible with more than 1% of the market, “last 1 Chrome Version” means compatible with the last version of Google, you can use the command NPX Browserslist “> 1%” to see which browsers are included. The Github address of Browserslist

  • Chunk: It’s not a library or a plug-in, it’s a noun, a code block as the name suggests. Why single it out, because once you understand it you’ll understand what it means to configure some parameters in webPack

  • Chunks: A chunk contains at least one chunk. A chunk is a collection of chunks

    // index.js
    import { a } from 'a.js'
    console.log('I'm index file')
    
    //a.js
    const a = 'I'm file A'
    export { a }
    Copy the code

    In the code above, a.js is chunk and index.js is chunks

    In a WebPack build, the entry is chunks and the exit is chunk (just know the concept)

Build a Webpack project

Initialize the project

  • Create a new folder

    mkdir learnWebpack
    Copy the code
  • Go to the folder

    cd learnWebpack/
    Copy the code
  • Initialize the package.json file

    #You can type in configuration
    npm init
    
    #Default Configuration Creation
    npm init -y
    
    #That's what you get with NPM init -y{" name ":" learnwebpack ", "version" : "1.0.0", "description" : ""," main ":" index. Js ", "scripts" : {" test ": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }Copy the code

The installation

Create a directory

  • Create SRC in the root directory, and create index.js in SRC

  • Add a code to index.js:

    console.log('hello webpack')
    Copy the code
  • Add “start”: “webpack” to the scripts field in the package.json file

      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"."start": "webpack"
      }
    Copy the code
  • On the command line, execute YARN start and we’ll see that the compilation succeeded, with a caveat! Webpack automatically creates a dist folder and the main.js file in the dist folder in the root directory

    • About the warning: ARNING in configuration The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment. You can also set it to ‘none’ to disable any default behavior. Learn more: Webpack.js.org/configurati… ; As the warning makes clear, we did not set the mode field. We simply configure the Mode field in the WebPack configuration to eliminate this warning

    • About the dist folder and main.js file: this is the zero-configuration use that webpack4 advertised. Obviously we didn’t configure anything here, so we successfully packaged a code under SRC. This feature is actually a simple package configuration that WabPack does for us by default. Let’s see what WebPack does for us by default:

      const path = require('path')
      module.exports = {
        extry: './src/index.js'.output: {
          filename: 'main.js'.path: path.resolve(__dirname, './dist')}}Copy the code
    • Note that the default configuration is too simple. After looking at this configuration, I think you can see why we can’t use zero configuration when we create something other than SRC or when SRC is not index.js

Zero configuration for Webpack

As we saw above, zero configuration is weak, and in a real project it is completely impossible to meet our needs

If we want to customize webpack configuration, we need to create a webpack.config.js file in the root directory. The contents of this file can override the zero configuration of WebPack

  • Use the default configuration file: webpack.config.js

    #When using the webpack.config.js configuration file, enter this command to start webpack packing
    webpack
    Copy the code
  • Using other configuration files, such as yzyconfig.js, you can specify which configuration file webPack uses to perform the build with –config yzyconfig.js

    #Run the --config command to specify other configuration files and package them according to the configuration contents of the specified configuration files
    webpack --config yzyConfig.js
    Copy the code

Webpack configuration core concepts

  • Chunk: refers to a code block. A chunk may consist of multiple modules. It is also used for code merging and splitting (the merging and splitting here mainly refers to the judgment of the fingerprint policy)
  • Bundle: The output file of the resource parsed and compiled by the Webpack process (a.js file, which is our output file)
  • Entry: Entry to file packaging. Webpack recursively searches for dependencies based on entry. Each dependency is processed by webPack and packaged into a collection file
  • Output: Configures the location and file name of the packaged output
  • Loader: WebPack only supports JS and JSON files by default. You can use loader to parse other types of files. In theory, WebPack can handle any type of file as long as there is a corresponding loader
  • Plugin: The loader’s main responsibility is to make webPack aware of more file types, while the Plugin’s responsibility is to control the build process to perform specific tasks. Plug-ins are very powerful and can accomplish a variety of tasks
  • Mode: target environment. The different target environment will affect the decision of webpack packaging
    • Production: code compression and a series of optimization operations
    • Development: facilitate hot update processing, identify which module change generation
    • None: Do nothing and get a warning when packing

Configuration webpack. Config. Js

Project Construction Objective

Our goal is not to build a complete and comprehensive project, but to learn Webpack with some representative functions as the starting point. I believe you will be able to draw inferences from one example after carefully reading these

  • Loading the CSS

  • Achieve CSS effect display

  • Implement automatic CSS prefix replenishment

  • Export the CSS as a file

  • Automatic generation of HTML files

  • Implement packing to empty the dist folder

  • The implementation image is introduced in the JS file

  • Implement images to be introduced in CSS files

  • Implement the Webpack local service

  • Implement multi-page packaging

Loading the CSS

We already know that the.CSS file cannot be properly bundled by Webpack (see the “Core Concepts of WebPack Configuration” section for an explanation of the bundle), so we need a loader to properly package it into the bundle

The file content

Install the CSS – loader

 yarn add css-loader -D
Copy the code

To configure the CSS – loader

const path = require('path')
module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: "development".module: {
    rules: [{test: /\.css$/,
        use: "css-loader"}}}]Copy the code

Run the webpack command to check if the CSS file is included in dist/main.js

If you create an HTML page in the dist folder, add the box class to the div element, and import the main.js file, you’ll see no styling effects at all.

Since the CSS content is simply imported into JS as a string (equivalent to json.stringify the CSS file), you won’t see the effect.

What do you do if you want to see results? Add CSS content to the style tag. We don’t need to do this because we have style-loader to do it for us!

Achieve CSS effect display

Install style – loader

yarn add style-loader -D
Copy the code

Configuration style – loader

const path = require('path')
module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: ['style-loader'.'css-loader'}]}}Copy the code

Note that when multiple Loaders are used for the same type of file, the use attribute takes an array and executes from right to left. Therefore, style-loader should be written before CSS-loader

Run the webpack command to see the results

Success!

However, we know that CSS3 has compatibility issues in browsers, which can be fixed by prefixing properties. The rich ecosystem of the front end certainly won’t let you do this on your own. We can do it for you with the autoprefixer plugin

Implement automatic CSS prefix replenishment

We already know that Autoprefixer is a plug-in for the PostCSS tool, so we need to install PostCSS and PostCSS-Loader

Postcss, postCSs-loader, and autoprefixer are installed. The postCSs-loader must specify the 4.x version, because the 4.x version and Webpack4 may generate errors

Yarn add autoprefixer [email protected] postcss -dCopy the code

Configure postCSs-Loader and autoprefixer

const path = require('path')
module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: [
          'style-loader'.'css-loader', 
          {
            loader: 'postcss-loader'.options: {
              postcssOptions: {
                plugins: [require('autoprefixer']}}}]}}Copy the code

When loader needs to write configuration, we can write loader as an object. The loader property is the name of the loader to use, and the options property is the configuration object of the loader. Autoprefixer is a postCSS plug-in, so the use of Autoprefixer is naturally written in the postCSS-Loader configuration

Since PostCSS has its own configuration file, it could also be written like this:

// webpack.config.js
const path = require('path')
module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: ['style-loader'.'css-loader'.'postcss-loader'}]}}// Create the postcss.config.js file in the root directory
module.exports = {
  plugins: [require('autoprefixer')]},Copy the code

Here we need to configure browserslist, otherwise the plugin will not know what rules to follow for prefix completion

// Add it to package.json file
// This indicates that the target browser is Internet Explorer, which must be compatible with Internet Explorer 8 or higher
"browserslist": ["ie > 8"]
Copy the code

Run the webpack command to see the results

Success!

Export the CSS as a file

As the project grew, we didn’t want to put so many styles in the style tag, we wanted to introduce them with the link tag, so we needed to use the Mini-CSS-extract-plugin

Install the mini – CSS – extract – the plugin

yarn add mini-css-extract-plugin -D
Copy the code

Configuration of the mini – CSS – extract – the plugin and the plugin loader, at this moment we don’t need style – loader, we want to style – loader replaced with MiniCssExtractPlugin. Loader

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'postcss-loader']]}},plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css"}})]Copy the code
  • MiniCssExtractPlugin can configure output file names
  • [name] is a placeholder. What name is imported and what name is exported
  • CSS/Indicates that the file is exported to the CSS folder

Run the webpack command to see the results

Success!

Automatic generation of HTML files

We found that the HTML under DIST was created manually by ourselves, which was obviously not elegant enough. Html-webpack-plugin helps you solve!

Install the HTML-webpack-plugin and make the 4.x version

Yarn add [email protected] - DCopy the code

Configure HTML – webpack – the plugin

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'postcss-loader']]}},plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",}).new HtmlWebpackPlugin({
      template: './src/index.html'}})]Copy the code

Run the webpack command to see the results

Success! Obviously HtmlWebpackPlugin generated a new HTML page for us based on our template and automatically introduced the dependencies under the DIST package. See more HtmlWebpackPlugin configurations

Implement packing to empty the dist folder

We will find that the contents of the dist folder will be overwritten each time we pack it, but if the file name is different next time we pack it, the old file will still exist, which we don’t want. The clean-webpack-plugin helps us solve this problem

Install the clean – webpack – the plugin

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

Configure the clean – webpack – the plugin

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'postcss-loader']]}},plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",}).new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ]
}
Copy the code

At this point, you will also create a random file in the dist folder, run the webpack command to see the result, you will find that the random file you created is no longer there. Verify that, and you’ve succeeded

The implementation image is introduced in the JS file

For this function we use url-loader, of course you can also use file-loader. Url-loader is an upgraded version of file-loader. It also relies on file-loader internally. File-loader and url-loader were both deprecated after webpack5 and replaced with asset modules

Install url-loader and file-loader

yarn add url-loader file-loader -D
Copy the code

You may wonder why file-loader is installed because url-loader depends on file-loader. If not, there will be no problem when url-loader converts images to base64 and imports the bundle, but exporting images directly to the dist folder will raise an error telling you that file-Loader is missing

Configure the url – loader

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'postcss-loader'] {},test: /\.(png|jpe? g|gif)$/,
        use: {
          loader: 'url-loader'.options: {
            name: '[name].[ext]'.limit: 1024 * 3}}}]},plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",}).new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ]
}
Copy the code
  • What format does [ext] indicate when it is imported and when it is exported
  • Limit indicates the size threshold of an image. Images that exceed this threshold will be placed in the bundle folder as they are, and images that do not exceed this threshold will be placed in the bundle file as base64

Import the image in the entry file

// index.js
import './index.css'
import mk85 from './assets/images/mk85.jpeg'
console.log(mk85) // mk85.jpeg
const img = document.createElement('img')
img.src = mk85
const BoxDiv = document.getElementsByClassName('box')
BoxDiv[0].appendChild(img)
Copy the code

Run the webpack command to see the results

Implement images to be introduced in CSS files

To import images in CSS we still use url-loader, but we need to modify the configuration slightly

CSS code

.box {
  width: 100px;
  height: 100px;
  /* background-color: yellowgreen; * /
  background-image: url('./assets/images/mk85.jpeg');
  display: flex;
}
Copy the code

Direct reference and packaging, packaging success! When you open the HTML page, you can’t see the image because the address is wrong. After packaging, the mk85 image is stored in the dist folder, and the reference path of the index.css is still mk85.jpeg, but the index.css is in the CSS folder, so it cannot be referenced naturally. So how do you quote it? The easiest way to do this is to add a /, but there is a pit. Add: create-react-app also implements reference unification through /

Example Modify the url-loader configuration

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'postcss-loader'] {},test: /\.(png|jpe? g|gif)$/,
        use: {
          loader: 'url-loader'.options: {
            name: '[name].[ext]'.limit: 1024 * 3.outputPath: "images/".publicPath: "/images",}}}]},plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",}).new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ]
}
Copy the code
  • OutputPath indicates where the output goes (provided by file-loader)

  • Name: images/[name].[ext] Is this the same as using outputPath? The value is different when the publicPath field is used. So if you don’t need to configure publicPath, you can set the output path by name (provided by file-loader).

    options: {
      name: '[name].[ext]'.limit: 1024 * 3.outputPath: "images/".publicPath: "/images",}/ / equivalent to the
      
    options: {
      name: 'images/[name].[ext]'.limit: 1024 * 3.publicPath: "/",}Copy the code
  • PublicPath Indicates the path of resource reference

Run the webpack command to see the results

Success! This is the result we want, but the problem is that when you open the HTML page and you don’t see the image, what’s the difference between /images, dot /images and image

In simple terms, if my service and my actual path is “localhost: 8080 / images/mk85 jpeg”, if not a service that is “. / images/mk85 jpeg”

So let’s start a service!

Implement the Webpack local service

Webpack – dev – server installation

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

Configure the url – loader

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'postcss-loader'] {},test: /\.(png|jpe? g|gif)$/,
        use: {
          loader: 'url-loader'.options: {
            name: '[name].[ext]'.limit: 1024 * 3.outputPath: "images/".publicPath: "/images",}}}]},devServer: {
    open: true.port: 8080,},plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",}).new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ]
}
Copy the code
  • Just add the devServer configuration
  • Open: Open the browser
  • Port Indicates the port number of the service

Note: this is not usedwebpackCommand to start the projectwebpack-dev-serverTo start the

Implement multi-page packaging

As the name implies, multi-page is naturally a number of HTML pages, each HTML page has its own JS file, so, how many entries will have how many exits

We will first set up the directory format to accommodate the multi-page packaging format (the following format is not unique, but it helps to understand)

  • SRC /index.js is not needed here
  • Create a webpack. Multiple. Config. Js
  • New SRC/pages/login/js/index, js
  • New SRC/pages/main/js/index, js

Install glob for processing files

yarn add glob -D
Copy the code

Configuration webpack. Multiple. Config. Js

module.exports = {
  entry: {
    login: './src/pages/login/js/index.js'.main: './src/pages/main/js/index.js'
  },
  output: {
    filename: '[name].js'.path: path.resolve(__dirname, './dist')},plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'.filename: 'login.html'.chunks: ['login'] // The name of the chunks corresponds to the name in the entry
    }),
    new HtmlWebpackPlugin({
      template: './src/index.html'.filename: 'main.html'.chunks: ['main']]}})Copy the code

And you’re done! You can use a webpack — config. / ` webpack. Multiple. Config. Js run the command. It will turn out exactly as you hoped

However, at this point you must be thinking, am I reconfiguring every page I write? This is too much trouble and not elegant! So let’s solve this problem now, and go straight to the code

// We write a method that automatically does what we configured above
const glob = require("glob")

const setMpa = () = > {
  const entry = {}
  const htmlwebpackplugins = []
	// Get our entry file array from the glob library
  const entryFiles = glob.sync(path.resolve(__dirname, "./src/pages/*/*/index.js"))
	// console.log(entryFiles)
  // Print the result
  / / /
  // '/Users/yzy/Desktop/learnSpace/learnWebpack/src/pages/login/js/index.js',
  // '/Users/yzy/Desktop/learnSpace/learnWebpack/src/pages/main/js/index.js'
  // ]
  entryFiles.forEach((item) = > {
    const entryFile = item
    const match = entryFile.match(/src\/pages\/(.*)\/js\/index\.js$/)
    // console.log(match)
    // Print the result
    / / /
    // 'src/pages/login/js/index.js',
    // 'login',
    // index: 43,
    // input: '/Users/yzy/Desktop/learnSpace/learnWebpack/src/pages/login/js/index.js',
    // groups: undefined
    // ]
    const pageName = match[1]
    entry[pageName] = entryFile
    htmlwebpackplugins.push(
      new HtmlWebpackPlugin({
        template: `./src/index.html`.filename: `${pageName}.html`.chunks: [pageName]
      })
    )
  })

  return {
    entry,
    htmlwebpackplugins,
  }
}
Copy the code

Now that we have this method, we add it to the configuration file

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const glob = require("glob")

const setMpa = () = > {
  const entry = {}
  const htmlwebpackplugins = []
  const entryFiles = glob.sync(path.resolve(__dirname, "./src/pages/*/*/index.js"))
  entryFiles.forEach((item) = > {
    const entryFile = item
    const match = entryFile.match(/src\/pages\/(.*)\/js\/index\.js$/)
    const pageName = match[1]
    entry[pageName] = entryFile
    htmlwebpackplugins.push(
      new HtmlWebpackPlugin({
        template: `./src/index.html`.filename: `${pageName}.html`.chunks: [pageName]
      })
    )
  })

  return {
    entry,
    htmlwebpackplugins,
  }
}
const { entry, htmlwebpackplugins } = setMpa()
module.exports = {
  entry,
  output: {
    filename: '[name].js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.css$/,
        use: [
          'style-loader'.'css-loader'.'postcss-loader'] {},test: /\.(png|jpe? g|gif)$/,
        use: {
          loader: 'url-loader'.options: {
            name: '[name].[ext]'.limit: 1024 * 3.outputPath: "images/".publicPath: "/images",}}}]},devServer: {
    open: true.port: 8080.hot: true
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",}).newCleanWebpackPlugin(), ... htmlwebpackplugins ] }Copy the code

We’ll use webpack — config. / ` webpack. Multiple. Config. Js command to run, success!

summary

If you want to write a loader and plugin, do you have any ideas

I can’t think of anything unexpected, but you’re probably already thinking of something. If not, don’t worry, check it out below

Implementing a Loader

The loader document is prepared on the official website

Loader is a function. Note that this is not an arrow function

Write a loader to replace the string

// replaceLoader.js
module.exports = function (soure) {
  console.log(soure, this) // You can print the information for yourself, but it is too long
	return source.replace('hello webpack'."Hello, Webpack!")}Copy the code
  • Loader is a function
  • You cannot use the arrow function because the context this is used
  • Soure receives the source code for the file to be processed
  • The source file processed by return is also the source file received by the next loader

Using the loader

{
  test: /\.js$/,
  use: path.resolve(__dirname, './loader/replaceLoader.js')}Copy the code

Run the webpack command to see the results

Success! If you find that the loader is so simple, you can try to write CSS, PNG and other files

Implement a Plugin

Online links, the official website to write plugin documents

As simple as that, we have used the plugin many times and found that we need to update it. Obviously, a custom loader is a constructor.

Let’s look at the format:

class PluginName {
	constructor (options) {}apply(compiler){... }}Copy the code
  • Be sure to write the Apply method, which will launch the plug-in from WebPack
  • PluginName can be written as a plain function, and apply must be mounted to the prototype object (PluginName.prototype)
  • Apply in the class class cannot be written as an arrow function
  • The compiler hooks in WebPack determine what your plug-in should do

Write a fake HTml-webpack-plugin and output a fake.html file

class HtmlPlugin {
	constructor (options) {}apply(compiler) {
		compiler.hooks.emit.tap('HtmlPlugin'.(compolation) = > {
      const content = '<html><body>fake html</body></html>'
      compolation.assets['fake.html'] = {
        source: function () {
          return content
        },
        size: function () {
          return content.length
        }
      } 
    })
	}
}
module.exports = HtmlPlugin
Copy the code

Use this Plugin

plugins: [
  new HtmlPlugin()
]
Copy the code

Run the webpack command to see the results

Success! You can also try to improve the plugin, add configuration, add resource files, etc

Fingerprint strategy

About Browser Caching

  • Modern browsers have caching mechanisms
  • When we visited the website of A for the first time, the files of Y.JS were loaded and cached
  • On our second visit to site A, the browser found y.JS already in the cache
    • Y.js is in cache so use cached files
    • Advantages: Reduced resource requirements
    • Disadvantages: When the y.js content is updated, you cannot obtain the latest y.js content without forcibly refreshing the browser
  • We can solve this problem by adding an identifier
    • Y.123.js is loaded on the first access
    • The second access finds a cache and uses y.123.js in the cache
    • The contents of the y file in the server are changed, and its name is also changed to Y.111.js
    • The third visit found no Y. 111.js file, load the latest Y. 111.js correctly

Appeals are relatively simple explanation, specific details you can not know, understand the cache mechanism of the cache can be

Fingerprint policies are used in Webpack

Use:

filename: '[name].[hash].[ext]'.Copy the code
  • Hash: Based on the item, if the item content changes, a new hash will be generated. If the content does not change, the hash will not change
    • If any file that needs to be packaged changes throughout the project, the hash value of all files in the package result changes
  • Chunkhash: the unit is chunk. When the content of a file changes, the module hash of the entire chunk group changes
    • Suppose the package exit has A.123.js and C.123.js
    • File A introduces file B
    • Modified the content of file B
    • The repackaging results are A.111. js and C.123.js
    • The hash value of A is affected, but the hash value of C is not
  • Contenthash: Based on its own content, not dependent
    • Suppose the package exit has A.123js and B.123.css
    • File A introduces file B
    • Modified the content of file B
    • The repackaging results are A.123.js and B.111.css
    • The hash value of A is not affected

Hot Module Replacement

You must have used this feature before, you just didn’t know it!

Scene:

  • Start the project local service
  • Modified the contents of a. CSS file
  • The browser did not refresh, but the changes took effect

This is hot module replacement, tip: no matter CSS or JS can do hot module replacement, but I recommend only to do CSS hot module replacement. Because js hot module replacement requires writing code to replace it, js hot module replacement is not very useful except in certain circumstances.

Let’s do a hot module replacement for CSS

Note: Hot module replacement does not support extracted CSS files, can only be placed in style, so style-loader is required

Configuration webpack. Config. Js

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlPlugin = require('./plugin/htmlPlugin')
const Webpack = require('webpack')

module.exports = {
  entry: './src/index.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, './dist')},mode: 'development'.module: {
    rules: [{test: /\.js$/,
        use: path.resolve(__dirname, './loader/replaceLoader.js')}, {test: /\.css$/,
        use: [
          // MiniCssExtractPlugin.loader,
          'style-loader'.'css-loader'.'postcss-loader'] {},test: /\.(png|jpe? g|gif)$/,
        use: {
          loader: 'url-loader'.options: {
            name: '[name].[ext]'.limit: 1024 * 3.outputPath: "images/".publicPath: "/images",}}}]},devServer: {
    open: true.port: 8080.hot: true
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",}).new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin(),
    new HtmlPlugin(),
    new Webpack.HotModuleReplacementPlugin()
  ]
}
Copy the code

On second thought, a complete configuration file is attached (a little worried that this takes up too much page space)

  • First of all, the MiniCssExtractPlugin before. Replace back style – loader loader
  • Add to devServerhot: true
  • The introduction of webpack:const Webpack = require('webpack')
  • Add:new Webpack.HotModuleReplacementPlugin()

Run the webpack-dev-server command to see what happens!

What’s updated with webpack5

Without further ado, go directly to the link, webpack5 updated content

Note: when you use Webpack4, either install plugin (plugin) or loader, try to find the current version of the plugin. If you find that the version has jumped from 4.x to 5.x, then be sure to install the 4.x version. Otherwise, unknown errors will occur during the packaging process

At the end

At first, this article only wrote the part of “Building Webpak Project” and then published it and marked it as unfinished to be continued. I wanted to write the following content more succinctively (writing in detail is actually quite tiring), but when I saw the comments urging me to improve and expressing great praise, I decided to write this article well and give it to everyone. Until now, my fans have changed from 0 to 18 through this article. Thank you very much for your recognition, which is also a great encouragement to me. I will try my best to write good articles and share them with you.

Finally, I hope that we all stay true to our original aspiration and grow up together! Like my nuggets ID, wish everyone KDDA(Keep da Dream Alive)