preface

Webpack is very important in the process of front-end, so I spent some time to learn WebPack, as well as webPack4 new features, this article is based on the process from easy to difficult, some WebPack concepts, common Loader, plugins, WebPack4 new special new, and some advanced concepts.

Core webPack concepts to master πŸ‘‡

  • Entry: The entry module that WebPack starts building
  • Output: How to name the output file and the output directory, such as the common dist directory.
  • Loaders: is used to parse files and process non-JS files that cannot be processed into modules that WebPack can handle.
  • Plugins: More optimization, extract essence (public module deweighting), compression processing (CSS/JS/HTML), etc., the extension of WebPack function.
  • ChunkI think this is for WebPack 4Code SplittingProduct, abandoned webPack3CommonsChunkPlugin, its biggest feature is simple configuration when you setmode 是 productionWebpack 4 will be automatically turned onCode Splitting, which can accomplish the deweighting of certain common modules into a singlechunk.

This study webPack4 new features, basically according to the official website to configure.


If, like me, you’re having trouble configuring your WebPack, you can come here for some ideas, and hopefully this article will be more of an introduction to the new features of WebPack 4.

Webpack basics

What is a webpack

Webpack is essentially a module packaging tool that packages multiple modules into a final bundle.js problem.

For now, webpack4.0 can package any type of module.

How do I install Webpack

First, make sure you have a Node environment and run the following command on your terminal

node -v
npm -v
Copy the code

Now that you have two version numbers, you can move on to WebPack, and the NPM package management tool is a must.

Initialize the project

npm init 
Copy the code

This means initializing a project in order to make it conform to the specification, which in turn makes it conform to the specification.

The next thing you’ll notice is that in this root directory, a package.json file is generated, which describes the Node project and some information about the Node package. In other words, NPM init generates a package.json file.

Package. json property description

Name - Package name version - Package version number. Description - Description of the package. Homepage - The official URL of the package. Author - the author of the package, whose value is your valid account name at https://npmjs.org, following the "account name < email >" rule, for example: zhangsan <[email protected]>. Ficolin-coding - other contributors to the package. Dependencies/devDependencies - List of production/development environment dependencies. They will be installed in the node_module directory. The main-main field specifies the program's main entry file, which is loaded by require('moduleName'). The default value for this field is index.js under the module root directory. Keywords - keywordsCopy the code

Next up is to install WebPack

NPM install webpack webpack-cli -g // Install webpack globallyCopy the code

If you have multiple projects and one of your WebPack dependencies is version 3. X and the current version is version 4.0, the project will not work.

Uninstall the global installation of WebPack and install it separately in the project you are currently running.

NPM uninstall webpack webpack-cli -g // Uninstall global WebPackCopy the code

How to install globally πŸ‘‡

NPM install webpack webpack-cli -dCopy the code

To check the version, the webpack -v command will not work, because node will go to the global search and find that the webpack package cannot be found, because we have uninstalled the global webpack, so we need to use a new command.

npx webpack -v
Copy the code

At this point, you can see for the version number.

How do I check the version of the package

NPM info webpack // View the WebPack versionCopy the code

Webpack configuration file

Webpack.config. js is the default configuration file for WebPack. We can customize the configuration file, such as the entry and exit of the file.

const path = require('path')
module.exports = {
    entry : './index.js',
    output : {
        filename : 'bundle.js',
        path : path.join(__dirname, 'dist')
    }
}
Copy the code

This is the basic configuration, package an index.js file, which is the entry and output package of the entry and exit configuration information.

At this point, running NPX webpack on the command line will look for the configuration information in the webpack.config.js file.

The default configuration file must be named webpack.config.js, but if you write your own webPack configuration file information, is that ok? Of course you can, if you run the following command πŸ‘‡

NPX webpack --config webpack.config.js // --configCopy the code

npm scripts

NPM scripts, if you’ve used vue, React, it’s usually NPM run dev, so can we also configure information like this? Just configure the scripts command in the package.json file πŸ‘‡

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

At this point, you are running NPM run dev, which will be translated into the instruction for, and the corresponding file will be packaged.

Webpack packages three commands

  • Webpack index.js (global)
  • npx webpack index.js
  • npm run dev

webpack-cli

At this point, you may find the webpack-cli function. If you do not download the package, you will not be able to run the Webpack command on the command line. In other words, the webpack-cli function is to run the Webpack command on the command line and it will take effect.

Using webpack commands on the command line is not allowed without downloading.

Webpack configuration environment

There are two main types of environment: Development and Production. The default is Production. The difference between the two is that the latter compresses the packaged files. ** So let’s go configure πŸ‘‡

const path = require('path')
module.exports = {
    mode : 'development',
    entry : './index.js',
    output : {
        filename : 'bundle.js',
        path : path.join(__dirname, 'bundle')
    }
}
Copy the code

At this point, if you look at the bundle.js file, you will see that the bundle.js file has no compression code.


Webpack core concept loader

What is the loader

Loader is a packaging scheme that knows how to package a particular file. Webpack doesn’t know what to do with some files, and Loader knows what to do with them, so WebPack turns to Loader for help.

By default, WebPack knows how to package js files, but for some modules, such as images, font ICONS, WebPack does not know how to package, so how do we make WebPack recognize images and other formats of modules?

Then go to the configuration file webpack.config.js to configure the corresponding information, and configure moduleπŸ‘‡

const path = require('path')
module.exports = {
    mode: 'production'.entry: './src/index.js'.module: {
        rules: [{
            test: /\.(png|jpg|gif)$/,
            use: {
                loader: 'file-loader'}}},output: {
        filename: 'bundle.js'.path: path.join(__dirname, 'dist')}}Copy the code

If we need a file-loader, we depend on it, so we download it first

npm install file-loader -D
Copy the code

Then let’s see how index.js is written πŸ‘‡

Import acator from './ avatage.jpg 'console.log(acator)Copy the code

And what we found from this is that, on the console, the print is

3f16daf5233d30f46509b1bf2c4e08a5.jpg
Copy the code

File loader helps us to package the image module in dist directory, and the index. Js, the acator variable, the result is a name, in this way, we can complete the packaging, the subsequent need for the image is easy to do.

conclusion

Webpack does not recognize modules that do not end in JS, so loader needs to tell WebPack to recognize them so that the package can be completed.

  • When it encounters a non-js ending module, WebPack will look for the corresponding rule in the Module, match the rule for, and then go to the corresponding loader
  • The corresponding Loader packages the module into the corresponding directory, dist in the above example, and,The path of the module is returnedTake the example aboveacatorThe value of the variable is the path.

How do I configure file-loader

Of course, to see the webpack website, which is very detailed documentation, click here

For example, if you want to keep the package name unchanged and add a suffix, you can configure optionsπŸ‘‡

			{
                loader: 'file-loader'.options: {
                    // The name is the original name, the hash algorithm is MD5, and ext is the suffix
                    name: '[name]_[hash].[ext]'}}Copy the code

Where we introduced the photos is below πŸ‘‡

import acator from '/ avatar. JPG'
Copy the code

So the name of the final package is the description πŸ‘‡

Head _3f16daf5233d30f46509b1bf2c4e08a5. JPGCopy the code

For example, if you want to package images and modules into images under dist directory, is this also configurable

			{
                loader: 'file-loader'.options: {
                    name: '[name]_[hash].[ext]'.outputPath: 'images/'}}Copy the code

If so, the corresponding packaged image will be found in the dist/images/ directory.

For example, the location of the image can be different in different environments, πŸ‘‡

if (env === 'development') {
        return '[path][name].[ext]'
}
Copy the code

The rest go to the official website, their own configuration.

How do I configure URL-Loader

For the image module packaging above, we can also use url-loader, so what is the difference between it and file-loader?

			{
                loader: 'url-loader'.options: {
                    name: '[name]_[hash].[ext]'.outputPath: 'images/'.limit : 102400  //100KB}}Copy the code

The only difference is whether the images are packaged in the images directory or in the Base64 bundle.js file, depending on the LIMIT configuration.

  • When the image size is larger than the limit parameter, it is the same as file-loader.
  • When the image is small, it is packaged in Base64 into bundle.js.

See the official website for more URl-loader

How do I configure csS-Loader

For example, if you introduce a CSS module, you need to download the corresponding module loader.

CNPM install css-loader style-loader -d // Download the corresponding moduleCopy the code

The next step is to configure moduleπŸ‘‡

		{
            test: /\.css$/,
            use: ['style-loader','css-loader']
        }
Copy the code

In this case, you can import the style in index.js to take effect, let’s see how to import πŸ‘‡

JPG 'import './index.css' const img = new Image() img.src = acator img.classList.add('imgtitle') document.body.appendChild(img)Copy the code

The imgTitle is the style, πŸ‘‡

.imgtitle{
    width: 100px;
    height: 100px;
}
Copy the code

Webpack packs CSS files using two loaders. Let’s analyze the following two loaders.

  • The csS-Loader is used to combine multiple CSS files into one CSS file.
  • The style-loader will mount the integrated CSS portion into the head tag.

If you use SCSS to pre-compile CSS, webPack will not be able to package the file, so you will need to install a new loaderπŸ‘‡

How do I configure sass-Loader

To see what scSS-Loader needs to download from the official website, click here

npm install sass-loader node-sass --save-dev
Copy the code

To install sass-loader, you need to install Nod-sass at the same time, and then configure the corresponding module

		{
            test: /\.scss$/,
            use: ['style-loader'.'css-loader'.'sass-loader']}Copy the code

In this case, you can import the SCSS style file as follows, which can be packaged πŸ‘‡

// console.log(acator) import './index.scss' // import the SCSS file const img = new Image() img.src = acator img.classList.add('imgtitle') document.body.appendChild(img)Copy the code

Modules are loaded from right to left, so first load the sass-Loader into a CSS file, then use the CSS-Loader to package it into a CSS file, and then mount it to the page using the style-loader.

If you use cSS3 in SCSS files, do you need to add the vendor prefix? How do we need to get there at this time? What loader should I add? Look at the following

How do I configure postcsS-Loader

This loader solves the problem of adding manufacturer prefix. For webpack’s official website, πŸ‘‰ click here

npm i -D postcss-loader autoprefixer
Copy the code

Next, you need to create a postcss.config.js configuration file (the same location as webpack.config.js) with the following information: πŸ‘‡

Module. exports = {plugins: [require('autoprefixer')({overrideBrowserslist: [" Android 4.1 ", "iOS 7.1", "Chrome > 31", "ff > 31", "ie > = 8"]]}});Copy the code

When I first set it, it did not work because I did not set the browser to support it, then check out πŸ‘‡ below

		{
            test: /\.scss$/,
            use: ['style-loader'.'css-loader'.'sass-loader'.'postcss-loader']}Copy the code

Finally, you can see that for example, CSS3 has the manufacturer prefix πŸ‘‡

-webkit-transform: translate(100px, 100px);
-ms-transform: translate(100px, 100px);
transform: translate(100px, 100px);
Copy the code

If you import a new SCSS file from an existing SCSS file, it will not reinstall postCSS-Loader for you to start packaging. In this case, how do you set this up

// index.scss
@import './creare.scss';
body {
    .imgtitle {
        width: 100px;
        height: 100px;
        transform: translate(100px.100px); }}Copy the code
  • We know that we have configured the Loader rules to conform to this expectation
  • When an SCSS module is introduced into js code, it will follow this rule
  • So how to introduce SCSS files in SCSS files, then the rule will certainly not start packaging from postcsS-Loader, so we need to configure some information.
		{
            test: /\.scss$/,
            use: ['style-loader',
                {
                    loader: 'css-loader'.options: {importLoaders:2.modules : true}},'sass-loader'.'postcss-loader']}Copy the code

We need to configure options in csS-loader and add importLoaders :2. This will take postcss-loader and sass-loader, and so on, whether you are importing SCSS files in js, If SCSS files are imported into SCSS, all loaders are re-executed from bottom to top.

So what does the modules:true configuration do? Sometimes you need to add this configuration if you want your CSS style to apply to the current module, rather than globally. See πŸ‘‡

// index.js
import acator from '/ avatar. JPG'
import create from './create'

import style from './index.scss'  // Modules :true is used to avoid creating modules in the CSS scope
const img = new Image()
img.src = acator
img.classList.add(style.imgtitle)
document.body.appendChild(img)
create()
Copy the code

So what is the create module πŸ‘‡

import acator from '/ avatar. JPG'
import style from './index.scss'
function create() {
    const img = new Image()
    img.src = acator
    img.classList.add(style.imgtitle)
    document.body.appendChild(img)
}

export default create;
Copy the code

As you can see, the create module creates an IMG tag and sets a separate style. After giving modules: true, the next thing we need to do is import some syntax changes.

import style from './index.scss'
Copy the code

Then look in the style object variable and find the name set in SCSS.

conclusion

  • importLoaders:2The solution to this configuration is to introduce the SCSS file into the SCSS file, which will be repackaged from postcss-Loader
  • modules:trueStyles will not be introduced globally in the current CSS context, and syntactically need to use the following import
  • import style from ‘./index.scss’

For example, how to configure information for font ICONS? For large font icon packaging, you can use file-loader to complete πŸ‘‡

		{
            test: /\.(woff|woff2|eot|ttf|otf)$/,
            use: [
                'file-loader']}Copy the code

For more information on how to package static resources, see πŸ‘‰ (Static Loader manages resources).


The core concept of WebPack is plugins

How do you use plugins to make packaging easier? Plugins means plugins, which makes packaging easier for us, so let’s take a look.

Plugins: They do things for you when webPack is running at some point.

How to use HtmlWebpackPlugin

The function of this plugin is to generate an HTML file for you, and then automatically import the packaged JS file into the HTML file.

How do you configure it? Check out the webpack website

The first step is to download HtmlWebpackPlugin

cnpm install --save-dev html-webpack-plugin
Copy the code

Then in webpack.config.js configure the following information πŸ‘‡

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

var webpackConfig = {
  entry: 'index.js'.output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
  plugins: [new HtmlWebpackPlugin({
            template: 'src/index.html'  // Package the template index.html in the SRC/directory})]};Copy the code

Then run NPM run dev, and you’ll see that in the dist directory, it automatically generates an HTML module for you and introduces the bundle.js file.

Template: ‘SRC /index.html’ this configuration information tells you which index.html to package

How to use CleanWebpackPlugin

This plugin will help you to delete files in a directory. It will delete all files that were last packed before packaging.

CNPM I clean-webpack-plugin -D //"clean-webpack-plugin": "^3.0.0Copy the code

And then to configure the clean-webpack-plugin, you need to go to the web site to see how to configure the clean-webpack-plugin, you can go here to πŸ‘‰ NPM

The configuration information is as follows: πŸ‘‡, this is the latest clean-webpack-plugin configuration

const {CleanWebpackPlugin} = require('clean-webpack-plugin');

// WebPack4 does not need to configure the path
plugins: [ new CleanWebpackPlugin()]
Copy the code

The latest Version of WebPack4 does not need to configure the path and automatically clears files in the dist directory.


Core WebPack concepts

Basic configuration of entry and Output

Sometimes you need more than one entry file, so how do we do that? At this point, you need to look at the Entry and Output configuration items

And of course, the WebPack website has documentation, so out here and entry here

entry: {
        index :'./src/index.js'.bundle : './src/create.js',},output: {
        filename: '[name].js'.publicPath: "https://cdn.example.com/assets/".path: path.join(__dirname, 'dist')}Copy the code

conclusion

  • Entry is configured this way to accept multiple packaged file entries, while the filename of the output file needs to use the placeholder name
  • This will generate two files with no error, for which the name is the entry name
  • If the background has already mounted the resource to the CDN, then your publicPath will change the path before the publicPath value

How to configure source-map using DevTool

Devtool configurates source-map to solve the problem that when you have a problem with your code, the error will be mapped to your original directory, not the bundled one. This is also obvious. If you do not set it, the error will only be displayed in the bundle.js file, which is not good for finding errors

devtool:'inline-cheap-source-map'
Copy the code

It is necessary to set up devTool for different environments. In a development environment, we need to see where our code is reporting errors, so we need to configure

Documentation is available on the WebPack website

Then we come to the conclusion πŸ‘‡

  • In the development environment, configuredevtool:'cheap-module-eval-source-map'
  • Production environment, configurationdevtool:'cheap-module-source-map'
// development devtool:'cheap-module-eval-source-map'
// production  devtool:'cheap-module-source-map'
Copy the code

How to use webpack-dev-server

Webpack-dev-server can be used for rapid application development. Many of the configurations are available on the Webpack website. Click here

Download it first

cnpm i clean-webpack-plugin -D
Copy the code

It can do a lot of things, it can start a server, and it can listen in real time to see if the package has changed, and if it has changed, it will appear to update.

DevServer: {contentBase: path.join(__dirname, "dist"), // dist/compress: true, // whether to use gzip to compress port: // open: true // open: true},Copy the code

Many configuration items, you can go to the official documents to view, such as proxy configuration items, more documents click here

Then in package.json the scripts configuration item is as follows

"start": "webpack-dev-server"
Copy the code

The devServer can detect file changes in real time

The thing to note is that when you package with webpack-dev-server, you don’t generate the dist directory, you pack your files into memory

conclusion

  • DevServer can start a local server and help us update and load the latest resources
  • The packaged files are kept in memory and no dist directory is generated

Hot Module replacement

Hmr-hot Module Replacement enables modules to be replaced, added, or removed while the application is running, without the need to reload the entire page.

As the name suggests, hot Module replacement is used when you change a module before you want to reload the entire page

For example, when you change some styles in your CSS code and do not configure HMR hot replacement, the entire page will be rendered again. This is not necessary, so let’s configure πŸ‘‡

devServer: { contentBase: path.join(__dirname, "dist"), compress: true, port: 9000, open: true, hot: // hotOnly: true,},Copy the code

This hotOnly can be set, the most important is to set hot:true

Then add two plugins, which come with WebPack, so you don’t need to download πŸ‘‡

const webpack = require('webpack')
plugins: [
        new webpack.NamedModulesPlugin(),  // Configurable or optional
        new webpack.HotModuleReplacementPlugin() // This is a plug-in that must be configured].Copy the code

Added NamedModulesPlugin to make it easier to see which dependencies to patch.

After configuring the above information, if you run the command again, you will find that hot replacement of modules is enabled. The files of different modules are updated, and only the current module file is downloaded

The only thing to note is that for CSS content modification, the csS-Loader layer will help us do real-time hot update. For JS module, we need to manually configure πŸ‘‡

if(module.hot){
    module.hot.accept('./print'.() = >{
        print()
    })
}
Copy the code

Module.hot. accept(module1,callback) accepts a module that needs to be hot-updated in real time, detects when the content changes, and executes the callback function

conclusion

  • The problem that HMR module hot replacement solves is that it allows various modules to be updated at run time without a full refresh.
  • This means that you do not need to go back to the local server to reload other modified resources
  • Note that hot updates to js files need to be detected manually, using the module.hot.accept syntax

For more configuration information, visit the Webpack website and check out HMR here

Babel handles ES6 syntax

Next we’ll configure it πŸ‘‡

npm install --save-dev babel-loader @babel/core
// @babel/core is a core library in Babel

npm install --save-dev @babel/preset-env
// Preset -env this module is used to translate the syntax into ES5 syntax. This module includes all the rules translated into ES5 syntax

npm install --save @babel/polyfill
// Use a polyfill to implement a Promise,map, and other syntax not implemented in earlier versions.

Copy the code

Configuration module πŸ‘‡

module: {
  rules: [{test: /\.js$/,
            exclude: /node_modules/,
            loader: "babel-loader".options: {
                "presets": [["@babel/preset-env",
                        {
                            "useBuiltIns": "usage"}}}}// exclude: node_modules js file does not need es5 syntax
// "useBuiltIns" argument:
Copy the code
  • There are thepreset-envAfter this module, we will find what we wroteConst syntax is translated as var
  • However, if you look carefully, you will find that the Promise and Map syntax are not supported by the lower browsers.
  • So we need to@babel/polyfillModule, which complements Promise and Map to accomplish this function, as mentioned abovepolyfill

And then how do we use it? Import πŸ‘‡ at the beginning of the js file

import "@babel/polyfill";
Copy the code

But if you’re careful, you’ll see the problem, when you run out of this, the size of the package immediately increases by more than 10 times. Why is that?

This is because the @babel/polyfill module needs to implement the Promise,map and other syntax in order to make up for it, which is why the packaged files are so large.

Then we need to do some configuration for @babel/polyfill, as shown in πŸ‘‡

"useBuiltIns": "usage"
Copy the code

This syntax will only be used to polyfill the files we are currently packaging, such as Promise and map. We will not polyfill any other syntax that is not present in ES6. This will reduce the size of the files we are packaging

conclusion

  • You need to follow the babel-loader @babel/core libraries. @babel/core is its core library
  • @babel/preset-env it contains the syntax rules for TRANSLATING ES6 into ES5
  • Babel/Polyfill addresses some OF the ES6 syntax that older browsers can’t implement, using polyfill itself
  • throughimport "@babel/polyfill";Polyfill the ES6 syntax by introducing it at the beginning of the JS file
  • All of the above scenarios address the problem of the business encountering Babel

For more configuration information, see the official documentation here

When you use polyfill to solve problems when generating third-party modules, or when generating UI component libraries, the above scenario is polluting with Babel. At this point, we need a different solution to πŸ‘‡

The @babel/ plugin-transform-Runtime library will solve our problem, so let’s install the required library first

npm install --save-dev @babel/plugin-transform-runtime

npm install --save @babel/runtime
Copy the code

We can create a.babelrc file in the root directory and write the configuration information in the.babelrc file πŸ‘‡

{
    
    "plugins": [
      [
        "@babel/plugin-transform-runtime",
        {
          "corejs": 2,
          "helpers": true,
          "regenerator": true,
          "useESModules": false
        }
      ]
    ]
  }
Copy the code
// When your "corejs": 2, you need to install this
npm install --save @babel/runtime-corejs2
Copy the code

This way, there is no need to import “@babel/polyfill” when using the syntax; This kind of syntax to complete, direct normal writing on the line, and from the packaging volume, actually acceptable

conclusion

  • From a business scenario, yes@babel/preset-env
  • Use when generating third-party libraries or UI from yourself@babel/plugin-transform-runtime, which changes both the helper and polyfill to be imported from a single place, and the imported objects are completely isolated from the global variables, avoiding global contamination

Webpack advanced concepts

How to use Tree Shaking

If you think of your program as a tree, it would be shaking. Green shows the actual source code and library used, which are the living leaves on the tree. Gray represents useless code and withered leaves on trees in autumn. To remove the dead leaves, you must shake the tree to make it fall.

In general terms, when you introduce a module, you’re probably just using some of its functionality, and you don’t want to package it into a project. With tree-shaking, modules that are not in use can be shaken off, thus removing useless code.

Note that by default webPack4 does tree-shaking in production.

optimization.usedExports

Enable WebPack to determine the usage of each module export. Depend on the optimization. ProvidedExports configuration. Information collected by optimisation.usedexports can be used by other optimizations or output code (exports not used by the module are not exported, and the export name is confused with a single char if the syntax is fully compatible). To minimize code volume, unused exports are removed. Production is enabled by default.

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

At this point, take a look at your bundle bundle.js file and you’ll see that it has some improvements.

Mark the file as side-effect-free

Sometimes, when our modules are not pure enough, WebPack cannot identify which code needs to be removed, so it is necessary to provide hints to WebPack’s Compiler as to which code is “pure part.”

This is done through the “sideEffects” property of package.json.

{
  "name": "webpack-demo"."sideEffects": false
}
Copy the code

As mentioned above, if none of the code contains side effects, we can simply mark this property false to tell WebPack that it is safe to remove unused exports.

Note that any imported file is affected by Tree Shaking. This means that if you use a similar CSS-Loader in your project and import CSS files, you need to add it to the Side Effect list to avoid inadvertent removal of it in production mode:

{
  "name": "webpack-demo",
  "sideEffects": [
    "*.css"
  ]
}
Copy the code

Compressed output

In this way, we can already use the import and export syntax to find “dead code” that needs to be removed. However, we need to not only find it, but also remove it from the bundle. To do this, we will use the -p(Production) webPack compilation tag to enable the UgLifyJS compression plug-in.

Starting with WebPack 4, you can also pass"mode"Configuration options to easily switch to compressed output, simply set to"production".

conclusion

  • To use tree-shaking, you need to use the ES Module syntax, which is ES2015 Module syntax (i.eimport ε’Œ export).
  • In the projectpackage.jsonFile, add a “sideEffects” entry.
  • Introduce a compression tool (minifier) that removes dead code (e.gUglifyJSPluginWebpack4 started, of course, with support for compressed output.

For the principles, check out this tree-Shaking Performance Optimization Practices – Principles section

developmentandproductionEnvironment to build

In the development environment and the build environment, we rely on different functions, for example πŸ‘‡

  • The development environmentWe need a powerful source map and a localhost server with live reloading or hot Module replacement capabilities.
  • The production environmentIn, our goal shifted to focusing on smaller bundles, lighter source maps, and more optimized resources to improve load times.

Based on these two points, we need to set up separate WebPack configurations for each environment.

If you’ve written vue or React, you’ll notice that there is a webpack.mon.js configuration file, which is used to make sure that you don’t have to configure duplicate code in your configuration.

Webpack – the merge of installation

So the first thing you need to install is WebPack-Merge, and then integrate it together.

cnpm install --save-dev webpack-merge
Copy the code

So this is what our directory looks like πŸ‘‡

Webpack - demo | - build | - webpack.com mon. Js / / three new webpack configuration file | - webpack. Dev. Js / / three new webpack configuration file | - webpack. Prod. Js / / three new webpack configuration file | - package. Json | - postcss. Config. Js. | - babelrc | - / dist | - / SRC | - index. Js | - math. Js | - / node_modulesCopy the code

So now let’s see what information is configured πŸ‘‡

webpack.common.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const commonConfig = {
    entry: {
        main: './src/index.js',},module: {
        rules: [{
            test: /\.js$/,
            exclude: /node_modules/,
            loader: "babel-loader"
        }, {
            test: /\.(jpg|gif|png)$/,
            use: {
                loader: 'url-loader'.options: {
                    name: '[name]_[hash].[ext]'.outputPath: 'images/'.limit: 1024 //100KB}}}, {test: /\.css$/,
            use: ['style-loader'.'css-loader'.'postcss-loader'] {},test: /\.scss$/,
            use: ['style-loader',
                {
                    loader: 'css-loader'.options: {
                        importLoaders: 2.modules: true}},'sass-loader'.'postcss-loader'] {},test: /\.(woff|woff2|eot|ttf|otf)$/,
            use: [
                'file-loader']]}},plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html' // Package the template index.html in the SRC/directory
        }),
        new CleanWebpackPlugin({
            // No configuration is required}),].output: {
        filename: '[name].js'.// publicPath: "https://cdn.example.com/assets/",
        path: path.join(__dirname, '.. /dist')}}module.exports = commonConfig
Copy the code

webpack.dev.js

const path = require('path')
const webpack = require('webpack')
const {merge} = require('webpack-merge')
const commonConfig = require('./webpack.common')

const devConfig = {
    mode: 'development'.devtool: 'cheap-module-eval-source-map'.devServer: {
        contentBase: path.join(__dirname, "dist"),
        compress: true.port: 9000.open: true.hot: true.// hotOnly: true,
    },
    plugins: [
        new webpack.NamedModulesPlugin(),
        new webpack.HotModuleReplacementPlugin(),
    ],
    optimization: {usedExports: true}}module.exports = merge(commonConfig, devConfig)
Copy the code

webpack.prod.js

const {merge} = require('webpack-merge')
const commomConfig = require('./webpack.common')
const prodConfig = {
    mode: 'production'.devtool: 'cheap-module-source-map',}module.exports = merge(commomConfig, prodConfig)
Copy the code

Note that using merge() in an environment-specific configuration easily includes our common configuration in Dev and prod. The Webpack-Merge tool provides several advanced capabilities for merging, but we did not need them in our use case.

NPM Scripts

Now, we redirect the scripts to the new configuration. We define NPM run dev as a development environment script and use webpack-dev-server in it to define NPM Run build as a production environment script:

  {
    "name": "webpack-demo",
    "scripts": {
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js",
    "start": "npx webpack --config ./build/webpack.dev.js"
  	},
  }
Copy the code

Note that I put the three files in the build directory, of course, in the case of the root directory, we just change the path after the –config command.

Another thing to note is the configuration of the clean-webpack-plugin. When you put it in the build directory, the root directory of the plugin is build, so we need to change πŸ‘‡

		new CleanWebpackPlugin({
            // No configuration is required
        }),
Copy the code

The latest clean-webpack-plugin, which does not need to set a clear directory, automatically clears the package path, namely the dist directory.

SplitChunksPlugin Code separation

When you have multiple entry files, or a package file that needs to be divided, for example, third-party libraries like Lodash and jquery need to be packaged into a directory, and your own business logic code needs to be packaged into a file, then you need to extract common modules. That’s where the SplitChunksPlugin plug-in comes in.

This is a new addition to WebPack4. We need to manually configure optimity.splitchunks. Next, let’s take a look at its basic configuration πŸ‘‡

module.exports = {
  / /...
  optimization: {
    splitChunks: {
        chunks: "async".minSize: 30000.minChunks: 1.maxAsyncRequests: 5.maxInitialRequests: 3.automaticNameDelimiter: '~'.name: true.cacheGroups: {
            vendors: { 
                test: /[\\/]node_modules[\\/]/.// Match the modules in node_modules
                priority: -10   // Priority. When a module hits multiple cache group rules at the same time, it is assigned to the higher priority cache group
            },
        default: {
                minChunks: 2.// Overwrite the outer global attribute
                priority: -20.reuseExistingChunk: true  // Whether to reuse modules that have been separated from the original code block}}}},};Copy the code

Let’s start with each parameter πŸ‘‡

  • The property Settings on the outer layer of cacheGroups apply to all cacheGroups, but they can be reset within each cache group
  • chunks: "async"This property is set toWhat typeThe line separated code has three values
    • initialEntry block
    • allall
    • asyncBlock of code to load on demand
  • minSize: 30000The module size is larger than 30kb
  • minChunks: 1, a module can be extracted into a new chunk only when it is referenced by at least several modules
  • maxAsyncRequests: 5, the maximum number of concurrent requests allowed by the code block loaded on demand
  • MaxInitialRequests: marchMaximum number of parallel requests allowed by the entry code block after partitioning
  • automaticNameDelimiter: "~"Code block name separator
  • name: true, The name of the code block packaged by each cache group
  • cacheGroupsCache group to customize the corresponding rules.

You can set the corresponding rules according to the actual situation. Each cache group will allocate the matched modules to the code blocks (chunks) according to the rules. The packing result of each cache group can be a single chunk or multiple chunks.

Here’s an example of how code is separated in a real project. If you’re interested, you can see an instance of SplitChunk. Right

Lazy-loding Lazy loading and chunks

Import An asynchronous load module

In Webpack, what is lazy loading? For example, when I need to introduce a certain module on demand, we can use lazy loading. In fact, the implementation of the scheme is the import syntax, when a certain condition is reached, we will request resources.

So let’s see, how to implement lazy loading πŸ‘‡

Before we talk about this, we need to use a plug-in to complete the identification of import syntax.

cnpm install --save-dev @babel/plugin-syntax-dynamic-import
Copy the code

Then add a plug-in to the. Babelrc configuration

{
  "plugins": ["@babel/plugin-syntax-dynamic-import"]}Copy the code

This way, we are free to use import to load modules on demand in the project.

// create.js
async function create() {
    const {
        default: _} =await import(/*webpackChunkName:"lodash"*/'lodash')
    let element = document.createElement('div')
    element.innerHTML = _.join(['TianTian'.'lee'].The '-')
    return element
}

function demo() {
    document.addEventListener('click'.function () {
        create().then(element= > {
            document.body.appendChild(element)
        })
    })
}

export default demo;
Copy the code

The function of this module is that when you click on the page, it triggers the create function, loads the loadsh library, and then loads the lodash library laztily in the page. Packaging is normal, but some resources can be loaded when you trigger certain conditions, so it’s a kind of optimization.

Chunk

Chunk refers to a block of code in Webpack. What exactly is a Chunk of code? πŸ‘‡

Chunk is a collection of Modules in the Webpack packaging process. Webpack packs modules one by one by reference, forming a Chunk.

There are three ways to generate chunks

  • Entry portal
  • Asynchronous loading module
  • Code spliting

Chunk is just a concept. Understanding the concept of Chunk will help you to have a certain understanding of Webpack.

CSS code compression extraction

In the online environment, we need to package our CSS files into a single Chunk, so we need to use plug-ins to do this.

mini-css-extract-pluginCSS Code Extraction

To extract the CSS into a separate file plug-in that supports CSS loading on demand and sourceMap, we can take a look at the GitHub official to see its documentation

** Currently absent function, HMR. ** So, we can apply it to the build environment and start installing πŸ‘‡

npm install --save-dev mini-css-extract-plugin
Copy the code

To use this plug-in, it is still recommended to configure it in Webpack.prod. js (production environment). This plug-in does not support HMR for the time being, and development in the development environment requires HMR, so we only configure it in Webpack.prod. js this time.

One thing to note is that when your WebPack version is 4, you need to configure the sideEffects property in package.json, so you don’t use CSS as tree-shaking.

{
  "name": "webpack-demo",
  "sideEffects": [
  	"*.css"
  ]
}
Copy the code

Then, let’s look at how webpack.prod.js configures parameters.

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const {
    merge
} = require('webpack-merge')
const commomConfig = require('./webpack.common')

const prodConfig = {
    mode: 'production'.devtool: 'cheap-module-source-map'.plugins: [
        new MiniCssExtractPlugin({
            filename:'[name].[hash].css'.chunkFilename: '[id].[hash].css'})],module: {
        rules: [{
            test: /\.(sa|sc|c)ss$/,
            use: [
                MiniCssExtractPlugin.loader,
                'css-loader'.'postcss-loader'.'sass-loader',],}]}}module.exports = merge(commomConfig, prodConfig)
Copy the code

When you introduce the CSS module in JS, and you finally see separate chunks of CSS in the dist directory, it means that the CSS code has been extracted successfully. The next step is to compress the CSS code.

Webpack4 does not compress CSS code in production by default, so we need to download the plugin for it

optimize-css-assets-webpack-pluginCSS Code compression

Optimize – CSS-assets – Webpack-plugin GitHub official document

This will compress the packaged CSS code in line of code, so download the package πŸ‘‡

npm install --save-dev optimize-css-assets-webpack-plugin
Copy the code

The next step is to set optimization.minimizer. The important thing to note here is that setting optimization.minimizer overwrites webPack’s default rules, such that the JS code is no longer compressed.

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const {
    merge
} = require('webpack-merge')
const commomConfig = require('./webpack.common')

const prodConfig = {
    mode: 'production'.devtool: 'cheap-module-source-map'.optimization: {
        minimizer: [
            new UglifyJsPlugin({
                sourceMap: true.parallel: true.// Enable multiple threads to run in parallel to increase compilation speed
            }),
            new OptimizeCSSAssetsPlugin({}),
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            // Settings such as webpackoptions. output can be ignored
            filename: '[name].[hash].css'.chunkFilename: '[id].[hash].css'})].module: {
        rules: [{
            test: /\.(sa|sc|c)ss$/,
            use: [{
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                        // You can specify a publicPath here
                        // publicPathcss in webPackoptions. output is used by default
                        // For example, the backend supports putting CSS blocks into a CDN
                        publicPath: "https://cdn.example.com/css/"}},'css-loader'.'postcss-loader'.'sass-loader',],}]},}module.exports = merge(commomConfig, prodConfig)
Copy the code

However, it will be found that JS compression will also have problems in the production environment, so in order to solve the problem, we unified in the following comb πŸ‘‡

Uglifyjs-webpack-plugin code compression

The problem this plugin solves is that when you need to set it up in optimization.minimizer, it overwrites the webPack base configuration and overwrites the JS code compression, so we need to download it.

npm install -D uglifyjs-webpack-plugin
Copy the code

Then configure the above information in webpack.prod.js. For more information, see the official website documentation

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const {
    merge
} = require('webpack-merge')
const commonConfig = require('./webpack.common')

const prodConfig = {
    mode: 'production'.devtool: 'cheap-module-source-map'.optimization: {
        minimizer: [
            new UglifyJsPlugin({
                sourceMap: true.parallel: true.// Enable multiple threads to run in parallel to increase compilation speed
            }),
            new OptimizeCSSAssetsPlugin({}),
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            // Settings such as webpackoptions. output can be ignored
            filename: '[name].[hash].css'.chunkFilename: '[id].[hash].css'})].module: {
        rules: [{
            test: /\.(sa|sc|c)ss$/,
            use: [{
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                        // You can specify a publicPath here
                        // publicPathcss in webPackoptions. output is used by default
                        // For example, the backend supports putting CSS blocks into a CDN
                        publicPath: "https://cdn.example.com/css/"}},'css-loader'.'postcss-loader'.'sass-loader',],}]},}module.exports = merge(commonConfig, prodConfig)
Copy the code

Extracting and packaging CSS code doesn’t make sense for the developer environment, and compression of JS code is inefficient and not recommended, so we’ll skip configuring them in the development environment.

Contenthash resolves the browser cache

When you package a project that is about to go live, there is a requirement that you only modify some files, and you only want the user to use the files in the browser cache for the rest of the files, so we need to use Contenthash.

There are three types of hashes in WebPack, which are πŸ‘‡

hash

Hash, mainly for the development environment, in the process of build, when you have a project file was found to change, modify the hash value of the entire project will do (the hash value of the whole project is the same), like this, every time update, the file will not let the browser cache files, to ensure the file update rate, improve the development efficiency.

chunkhash

Specifically, WebPack analyzes the dependencies of the entry entry configuration file, builds the chunk from it, and generates the corresponding hash value. Different chunks have different hash values.

In a production environment, we package third-party or public libraries separately, so if you don’t change the code in the public library, the chunk hash won’t change and the browser cache can be properly used.

The problem with hashing is that in production we use the WebPack plugin to extract and package the CSS code separately. In this case, chunkhash is not flexible, because whenever the JS in the same chunk changes, the CSS chunkhash will also change. So we need contenthash.

contenthash

Contenthash Indicates the hash value generated by the file content. The contenthash value varies according to the content. In a production environment, it is common practice to pull the CSS in the project out of the corresponding CSS file to reference.

For webPack, in older versions, the contenthash value will change every time you NPM run build, even if you don’t change the content. This is because when you have modules referencing each other, there is a manifest file.

The manifest file is used to guide all of the module interactions. The manifest file contains the logic to load and process the modules. For example, your third-party library packaged files, which we’ll call vendors, your logic code is called Main, and when you webpack to generate a bundle, It also maintains a manifest file, which you can think of as each bundle, so each bundle has a different manifest, so we need to extract the manifest file.

At this point, you need to add a configuration πŸ‘‡ to Optimization

module.exports = {
  optimization: {
    splitChunks: {
      // ...
    },
    runtimeChunk: {// The problem is that the contenthash will still change if the content is not changed in the old version
      name: 'manifest'}}}Copy the code

Of course, if you haven’t already, you can go to the WebPack website and see the definition of manifest and what it means.

With that said, let’s take a look at how we can configure output. Let’s take a look at the webpack.prod.js configuration

output: {
        filename: '[name].[contenthash].js'.chunkFilename:'[vendors].[contenthash].js'.// publicPath: "https://cdn.example.com/assets/",
        path: path.join(__dirname, '.. /dist')}Copy the code

For webpack.dev.js, you only need to change the Contenthash to hash, which will improve the development efficiency.

Shimming global variables

For example, if you are using a third-party library and need to introduce it, or if you have a lot of third-party libraries or libraries written by yourself, every JS file needs to rely on it, making people very tedious, this time shimming comes in handy.

We need to use the ProvidePlugin plugin, which is built-in to WebPack and which Shimming relies on.

With ProvidePlugin, you can access a variable in each module compiled with WebPack to get a package.

Add a Plugin configuration πŸ‘‡

new webpack.ProvidePlugin({
			// This is where your rules are set
			// This is equivalent to the statement πŸ‘‡ when you use the lodash module
			// import _ from 'lodash'
            _: 'lodash'
})
Copy the code

Take πŸ‘‡ for example

// array_add.js
export const Arr_add = arr= >{
    let str = _.join(arr,'+ +');
    return str;
}
Copy the code

If you do not import the lodash library normally, you will get an error, but we use ProvidePlugin so that it will provide the appropriate lodash package. Note that to avoid multiple lodash packages being packaged, you can use CommonsChunkPlugin. Webpack4 has ditched it and replaced it with the splitChunksPlugin plugin, which I’ve already covered.

See the Shimming gasket for more usage


Comb summary limited, but more is their own configuration process encountered some problems comb summary, WebPack4 new features worth you to learn, the following are some meals πŸ‘‡

snacks

  • Webpack Revealed – the only way to the high end
  • Import, require, export, module.exports
  • Webpack,
  • Webpack4 configuration detailed slow chew fine pharynx
  • Another dozen Webpack interview questions

❀️ Thank you all

If you found this helpful:

  1. Like support, let more people can also see this content.
  2. Follow the public accountThe front-end UpUp“, to push good posts for you regularly.
  3. If you like it, you can also read TianTian’s recent article (thanks to the encouragement and support from friends of Mine 🌹🌹🌹) :
    • “Fill in the Gaps” to strengthen your HTTP knowledge (780+πŸ‘)
    • 21 handwritten JavaScript interview questions (560+πŸ‘)
    • 18 browser-based interview questions (740+πŸ‘)
    • 54 JavaScript interview questions (610+πŸ‘)
    • “Vue Project” how DO I solve the rolling components & Linkage effect (220+πŸ‘)
    • Chrome DevTools debugging tips for men, efficiency πŸš€πŸš€πŸš€(210+πŸ‘)
    • “How browsers work” To my Girlfriend – Rendering Process (1.1w + words) (230+πŸ‘)
    • “Array method” from detailed operation of JS array to analysis of V8 array.js(220+πŸ‘)
    • How the Browser Works the Secret to your Girlfriend – Browser composition & Network Request (1.2W words)(240+πŸ‘)