The core concepts of webpack — devserver, HMR and Babel

Time:2021-4-19

[toc]

1. devServer

I don’t know if you have found a tedious step in debugging in the previous lessons, that is, you need to re execute the NPM run bundle command every time the test is packaged, and then manually open the index.html File to see the effect, can we automatically complete the above process when the source code changes in the SRC directory?

1.1 using the — watch parameter

In the first method, we can add the — watch parameter to the webpack command

// package.json
"scipts":{
    "watch":"webpack --watch"
}

He will listen for changes in the packaged source code, and he will repackage it once the source code changes. To update the file under the dist directory.

This is the first solution. Will not help us from the server, there is no way to do Ajax debugging, must manually refresh the browser.

1.2 webpackDevServer

The above method can only complete automatic packaging, but it is not enough if you want to open the file automatically and simulate the characteristics of some servers. Here you need to use the devserver tool, which is a local server based on webpack. It can help us monitor the source code changes and refresh the browser automatically.

Basic use

First of all, you need to install:

npm install webpack-dev-server -D

Next, configure:

// webpack.config.js
devServer:{
    Contentbase: '. / dist' // server root path
}

And then in the package.json Add a command to the file:

"start":"webpack-dev-server"

Then execute the start command, and the log prompts us to start a service on the local port 8080, which can be accessed. At this time, we change the source code, and it will help us refresh the browser automatically, so it can improve the development efficiency.

Now we find that there is no dist directory. In fact, devserver itself will package SRC, but it will not be placed under the dist directory. Instead, it will be packaged and put into memory, so the packaging speed will be faster. Don’t worry.

At the same time, compared with the previous method, this method directly opens the local server index.html There is also an advantage is that you can send Ajax request, because the request must be in a server with HTTP protocol, and the local open is File protocol, certainly not! When the server is turned on, it is sent from the local port 8080. Generally, there is no problem – this is how most front-end frameworks are used.

Other configurations

  • Open: this configuration allows you to automatically open the browser and display the interface after executing the command.
  • Proxy: generally, the front-end framework sometimes configures this parameter for interface proxy and cross domain request
devServer:{
    ...
    proxy:{
        '/api':'http://localhost:3000'
    }
}

If the user accesses the API port of 8080 after the above filling, it will be forwarded to the local port of 3000.

  • Port: configure the port that devserver starts

1.3 implement a similar service by yourself

Here we write a simple service based on node to simulate the above functions of devserver.

First, we add a new command and the corresponding JS file

"server":"node server.js"

Now we need to do this server.js To help us create such a server with functions close to devserver

Setting up the environment

First of all, you need to install express or koa to help you quickly build the server. At the same time, you also need to monitor the changes of webpack packaging, so you also need a middleware:

npm install express webpack-dev-middleware -D

Then we add a publicpath parameter to the output field of the webpack configuration file, so that all fields that are packaged and referenced will have a / root path

output:{
    publicPath:'/',
    ...
}

to write server.js

const express = require('express')
const webpack = require('webpack')

Const app = express() // create a server instance
//Listening port
app.listen(3000,() => {
    console.log('server is running)
})

Next, we write the logic related to webpack. Here we need to use some APIs of webpack and webbackdevmiddleware

const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')
const config = require('./ webpack.config.js '; // import the configuration file
Const complier = webpack (config); // use webpack with config to compile code at any time. Returns a compiler

Const app = express() // create a server instance
//Using complier
//Middleware helps monitor whether the packaged source code has changed
//As long as the file changes, the complier will run again, and the corresponding packaged output is publicpath
app.use(webpackDevMiddleware(complier),{
    publicPath:config.output.publicPath
})

//Listening port
app.listen(3000,() => {
    console.log('server is running)
})

Now if you ask the compiler to execute it again, it will repackage the code~

Then we change the source code, you can see that the console will output packaging information.

But here is the browser must manually refresh, and want to be exactly the same as devserver need to configure a lot of things. Here will not continue to expand, interested in their own online search learning materials.

At the same time, you also know that webpack can also be used in node, which can implement many custom webpack extension services~

2. Hot module replacement – hot module update

Here, let’s look at a problem. When you modify the style of the source code, devserver will help us refresh the browser, but at this time, you don’t have any HTML updates (for example, add an element to the page), so you have to do it again.

Here, through HMR, you can directly update the style of the page after modifying CSS without refreshing.

2.1 use of HMR

CSS

devServer:{
    ...
    hot:true , // open HMR
    hotOnly:true // Even if the HMR doesn't work, I won't let the browser refresh automatically, and I won't do anything else
}

In this way, the devserver configuration is completed. In addition, a webpack plug-in needs to be introduced

// webpack.config.js
const webpack = require('webpack')
...

plugins:[
    ...
    new webpack.hotModuleRepalcemnetPlugin()
]

With the above configuration, the HMR function is turned on. We need to restart devserver for the configuration file to take effect.

We found that CSS modification will not affect the interface, only replace the CSS content, will not change the content rendered by JS! , which greatly facilitates the style debugging.

JS

For JS files, there will also be the above CSS problem: every time you modify a JS file, you will send an HTTP request, request 8080, refresh again, and the status of other JS files is not saved.

We hope that the data change of the independent module will not affect the content of other modules. In this case, we can use HMR to implement it.

Here, we open the previous HMR configuration. In addition, we need some additional code to make it work

//index.js

...

counter()
number()

if(module.hot){
    module.hot.accpet('./number',() => {
        document.body.removeChild(document.getElementById('number'))
        number();
    }) // if the number file changes, the following function -- number will be executed again
}

2.2 precautions

The HMR of JS can’t be updated directly like CSS. In fact, CSS also needs to write similar logic, but it has been handled for you in CSS loader.

In Vue and other frameworks, we have never written JS HMR code similar to the above, because Vue loader also has these processes built in.
Inside react, HMR is built in Babel process.

However, it should be noted that if you want to hot update some biased files, such as data files, the default processing logic may not be included, you need to write the above code manually, and you can’t use loader or bable process.

Interested can also continue to learn the underlying principles of HMR online

3. Babel

Here we explain how to combine Babel and webpack to handle our ES6 syntax

3.1 problem introduction

First, let’s build a new one index.js File, which uses some ES6 syntax:

const arr = [
    new Promise(() => {}),
    new Promise(() => {})
];

arr.map(item=>{
    console.log(item)
})

The above index file includes ES6 syntax. We try to package it with webpack

npx webpack

There is no devserver package here, because the packaged file will not be generated in the directory, but saved in memory, so you can’t see it. Just pack it directly with webpack

Let’s take a look at the content generated by packaging, which is almost intact.

Can the previous code run? Let’s open chrome and have a look (start with devserver).

As a result, we found that it seems normal and there is no problem. This is because the newer version of Chrome browser itself implements the syntax parsing function of ES6. However, if we use the lower version or the older IE browser, we may report an error. The final reason is that the compatibility of the program is poor.

We hope that after webpack is packaged, ES6 can be converted into Es5 syntax, so that all browsers can run.

3.2 introduction and use of Babel

Babel is a common JS compiler. Babel can convert ES6 syntax into Es5 syntax.

Babel with webpack, you can refer to the official website, with webpack tutorial.

Here, we need to install a Babel loader and Babel’s core library @ Babel / core, which can complete the process of JS code, AST code and Es5 code

npm install --save-dev babel-loader @babel/core

Then add a rule in the rules of config as described above:

rules:[{
    test:/\.js$/,
    exclude:/node_modules/,
    loader:"babel-loader"
}]

Exclude means that if your JS file is in node moudles, you will not use Babel loader, because this is a third-party code, and you have basically done this escape processing.

Next, you need to install @ Babel / preset to enable the conversion function of Es5

npm install @babel/preset-env --save-dev

This is because Babel loader is the bridge between webpack and Babel. It does not translate ES6 into Es5. It also needs the help of other modules, that is, the function of preset env.

rules:[{
    test:/\.js$/,
    exclude:/node_modules/,
    loader:"babel-loader",
    options:{
        presets:["@babel/preset-env"]
    }
}]

Then we’ll pack another bag and have a look main.js After packaging, the file finds that the arrow function becomes a normal function, and let becomes var

In addition, you can configure target to indicate which browsers need to be escaped. Babel will be used for conversion processing only when the configured browser version is lower than the version number you specified. Generally, this will be configured, because browsers with higher versions are actually compatible with ES6.

rules:[{
    test:/\.js$/,
    exclude:/node_modules/,
    loader:"babel-loader",
    options:{
        presets:[["@babel/preset-env",{
            targets:{
                edge:"17",
                chrome:"67",
                firefox:"60",
                safari:"11.1"
            }
        }]]
    }
}]

3.3 use of Babel / Polyfill

But is this conversion enough? It’s not enough. For example, the map method of array doesn’t exist in the lower version of browser, so we not only need to do the syntax conversion of preset Env, but also need to find a way to add the missing variables or functions to the code.
Here, we need to use Babel Polyfill:

npm install --save @babel/polyfill

Note not to add – Dev here, because this is also required when the code is running.

We just need to import where we need Polyfill. Here we put the import into the business code index.js Top of

// index.js

import "@babel/polyfill";

const arr = [];

arr.map(...)

In fact, this writing method is not very good. In fact, all the Polyfill functions are packaged in 1. We will find that the package after build is very large. Here, we don’t want Babel to include all the compatible syntax. For example, we only want the Polyfill map method, otherwise the package is too large

Here you need a configuration to write in webpack.config.js To change the press field and add a configuration:

rules:[{
    test:/\.js$/,
    exclude:/node_modules/,
    loader:"babel-loader",
    options:{
        presets:[["@babel/preset-env",{useBuiltIns:'usage'}]]
    }
}]

It means that when I do a Polyfill, I don’t add it all at once, but add it according to what I use, and then make a package

Note: if you are developing a class library or a third-party module, it is not recommended to use this method, because Polyfill uses the method of global replacement to complete compatibility, and class libraries and other methods will pollute the global environment.

3.4 babel/plugin-transform-runtime

To package a class library, you need to configure it in another way

Here we need to install two modules, according to the official website.
Then you need to add a plugin to the webpack configuration parameter

rules:[{
    test:/\.js$/,
    exclude:/node_modules/,
    loader:"babel-loader",
    options:{
        "plugins":[["@babel/plugin-transform-runtime",{
            "corejs":2,
            "helpers":true,
            "regenerator":true,
            "useEsModules":false
        }]]
    }
}]

Then repackage. At this time, you will be prompted with an error. A corejs2 module is missing. This is because our corejs is written as 2. At this time, refer to the document to install another package:

npm install --save @babel/runtime-corejs2

Then you can.
If you are writing business code, refer to 3.3 and use Polyfill.

If you are writing a class library, please refer to the above to avoid polluting the overall environment.

3.5 solve the problem of too many options configuration items

It is likely that there will be many options configured for online business. At this time, we can write a separate. Babelrc file in the root directory to copy the configuration items in

// .babelrc
{
    "plugins":[["@babel/plugin...."]]
}

3.6 configure the package of react code

React comes with JSX syntax. Webpack can’t recognize it without special processing. In fact, Babel has tools to help us parse JSX syntax of react

npm install --save-dev @babel/preset-react

Then add @ Babel / preset react to the presets field.

{
    presets:[
        [
            "@babel/preset-env",{
                targets:{
                    chrome:'67',
                },
                useBuiltIns:'usage'
            }
        ],
        "@babel/preset-react"
    ]
}

Note: in Babel, the syntax escape is executed in sequence, from bottom to top, from right to left, first convert react code, and then convert ES6 to Es5. Restart devserver