[ten thousand words summary] webpack can only basic configuration, but not! Come and pack the actual case configuration together

🛴 preface

stay The previous two articles In, we explained the introduction of webpack. However, after the introduction knowledge is understood, it must be applied to specific cases.

Therefore, in the following article, we will lead you to learn about some actual case configurations of webpack, including the packaging configuration of third-party libraries, PWA and ts, as well as the advanced operation of webpack devserver. We also need to focus on how to optimize the performance of webpack.

Let's start this article~ 🚦

🚌 1, Packaging of Library

Suppose we want to develop a component library or a function library, how should we package such library code with webpack?

1. webpack packaging Library

If we write a lot of logic code now, at the same time, we package these logic codes, and after packaging, all of them are generated in mondaylib. Under dist folder JS file.

Well, now the library is generated. How can our users introduce mondaylib?

In general, others can introduce our library in the following ways:

//Mode 1
import mondaylib from 'mondaylib'
//Mode II
const mondaylib = require('mondaylib')
//Mode III
require(['mondaylib'], function(){
    
})

Therefore, if we want our users to introduce this library, we need to use webpack config. JS. The specific configuration is as follows:

const path = require('path');

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'mondaylib.js',
        //The libraryTarget is configured here. umd means that the syntax of commonJS is supported
        libraryTarget: 'umd'
    }
}

libraryTarget: 'umd' means that the syntax of commonJS is supported, so the above three methods can be introduced. At the same time, the libraryTarget can also be set to other values, such as this and window.

libraryTarget: 'this' means that I want to mount the mondaylib variable on the page. libraryTarget: 'window' is expressed as the variable mondaylib, which will be mounted on the window.

In addition to the above three cases, there are other special cases, as follows:

<script src="mondaylib.js"></script>

At this point, we need to be in webpack config. JS:

const path = require('path');

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'mondaylib.js',
        //Mount the packaged generated code on the global variables of the page
        library: 'mondaylib',
        //The libraryTarget is configured here. umd means that the syntax of commonJS is supported
        libraryTarget: 'umd'
    }
}

The first library is the attribute value, and the second mondaylib is our library name. This configuration means that the generated code will be packaged, that is, mondaylib JS library, which is attached to global variables.

2. Library reference conflict

Suppose we now introduce lodash into the above mondaylib library. Then, when the user uses it, the user introduces the lodash library again. Like the following code:

import _ from 'lodash';
//The mondaylib library has previously introduced the lodash library
import mondaylib from 'mondaylib';

So now, how can we avoid this problem? Let's webpack config. JS. The specific code is as follows:

const path = require('path');

module.exports = {
    externals: ["lodash"]
}

From the above code, we can know that through the configuration of externals: [lodash], we can tell webpack that if lodash is encountered in the mondaylib library during the packaging process, we should avoid it and do not package. In this way, you can effectively avoid the problem of multiple references to the library and reduce the packaging size of the code.

externals has another special configuration, as shown in the following code:

module.exports = {
    externals: {
        // It indicates that if lodash is used in the environment of commonjs, it must be called lodash when lodash is loaded
        lodash: {
            commonjs: 'lodash'
        }
    }
}

If configured in this way, it is intended to indicate that if the lodash library is used in the environment of commonjs, it must be named lodash when loading, rather than arbitrarily. Like this:

//✔ Can be used: named lodash
import lodash from 'lodash'
//✘ not available: not named lodash
import _ from 'lodash'

🚍 2, Packaging configuration of PWA

1. What is PWA

PWA is called Progressive Web Application.

PWA is a relatively new front-end technology. What kind of technology is it?

The effect PWA can achieve is that if you visit a website, it may be successful for the first time, but suddenly the server of the website hangs up. Then you can't visit the website at this time. However, PWA will cache the first page you visit. After that, even if the server hangs up, you can still show the page you saw before.

Therefore, there is a plug-in in webpack to achieve this effect. Let's find out~

2. PWA in webpack

Step 1: install the plug-in. The specific codes are as follows:

npm install workbox-webpack-plugin --save-dev

Step 2: in webpack The plug-in is introduced into prod.js and used. The specific codes are as follows:

const WorkboxPlugin = require('workbox-webpack-plugin');

module.exports = {
    plugins: [
		new WorkboxPlugin.GenerateSW({
			clientsClaim: true,
			skipWaiting: true
		})
	],
}

Usually, we only need to introduce PWA into prod, which is an online environment, and this problem does not need to be considered in the development environment. Through the above configuration, after the project is packaged, two new files will be generated in the dist directory, one is service worker JS, and the other is precache JS file, these two files are for us to use PWA.

Step 3: import the above files. The specific codes are as follows:

if('serviceWorker' in navigator){
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js')
            .then(registration => {
                console.log('service-worker registed');
            }).catch(error => {
                console.log('service-worker regist error');
            })
    })
}

We need to write a business code in the entry file and introduce service worker JS file to help us do PWA. At this time, we package the project. After that, if the server suddenly hangs up, don't worry. PWA will help us load the original page for us to browse.

🚎 3, Packaging configuration of TypeScript

1. Cited examples

We all know that for different developers, different people write different code styles and forms, so it is difficult to ensure the maintainability of the project in the later stage. Well, at this time, Typescript, which was popular in 2018, appeared. TS standardizes a set of js standards. Therefore, we can standardize our code through ts in the writing of project code, and make the maintainability and scalability of our project better.

Next, let's learn how to support ts syntax through the configuration change of webpack.

2. Configuration of ts by webpack

(1) Background

Suppose we have a section of ts code that needs to be compiled. The specific code is as follows:

class Greeter{
    greeting: string;
    constructor(message: string){
        this.greeting = message;
    }
    greet(){
        return "Hello, " + this.greeting;
    }
}

let greeter = new Greeter("world");

let button = document.createElement('button');
button.textContent = "Say Hello";
button.click = function(){
    alert(greeter.greet());
}

document.body.appendChild(button);

Now, we want webpack to compile this ts code. What should we do?

(2) Configuration steps

Step 1: install TS loader. The specific commands are as follows:

npm install ts-loader typescript -D

Step 2: we are on webpack config. JS file. The specific codes are as follows:

const path = require('path');

module.exports = {
    mode: 'production',
    entry: './src/index.tsx',
    module: {
        rules: [{
            test: /\.tsx?$/,
            use: 'ts-loader',
            exclude: /node_modules/ 
        }]
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
}

Step 3: configure tsconfig JSON file. The specific codes are as follows:

{
    "compilerOptions": {
        "outDir": "./dist",
        "module": "es6",
        "target": "es5",
        "allowJs": true
    }
}

3. ts identify the third party Library

Sometimes, we will call the join method in lodash, but if we do not carry out special processing, we will not report an error if we normally introduce and use the lodash Library in the tsx file. Therefore, we need to install another ts library to handle it. The specific steps are as follows:

Step 1: install the @ types/lodash library. The specific commands are as follows:

npm install @types/lodash --save-dev

After installing this library, ts can identify some functions and methods of lodash. Once there is a wrong reference, it will give an error prompt.

If you have a small partner, you will have a question: does ts have the type files of all such libraries (such as jQuery and other libraries)?

The answer is, of course, No. We can go to a website on github 👉https://microsoft.github.io/TypeSearch/ If the search results are found, we can install it with @ type / library name, and then the tsx file will support the type check of the library.

🚕 4, Advanced operation of WebpackDevServer

1. WebpackDevServer implements request forwarding

In general, we can build a proxy server locally through charles fiddler tool. Through this proxy server, forward the interface address we want to request.

In webpack, we are provided with a tool, devserver Proxy . Next, we're at webpack config. JS. The specific code is as follows:

module.exports = {
    devServer: {
        proxy: {
			'/react/api': {
				target: 'http://www.mondaylab.com',
				//Realize the request forwarding of https web address
				secure: false,
				bypass: function(req, res, proxyOptions){
					//If the requested content is an html address, it will directly return to the index.html address under the root path html content
					if(req.headers.accept.indexOf('html') !== -1){
						console.log('Skipping proxy for browser request');
						return './index.html';
						// return false; // It means that if you encounter an html request, you can return whatever you should return
					}
				},
				//Write header when the front end requests JSON, webpack will indirectly help us get the demo JSON data (request forwarding])
				pathRewrite: {
					'header.json': 'demo.json'
				},
				//If some websites are anti crawler, we may not be able to cross domain. The following configuration is required to break through the limitation on origin
				changeOrigin: true,
				// Customize some content in the request header
				headers: {
					host: 'www.mondaylab.com',
					//Simulate some login operations when forwarding requests
					cookie: 'gfhgfh'
				}
			}
		}
    }
}

2. WebpackDevServer solves the problem of single page application routing

For modern mainstream frameworks, like Vue js ,React.js and other frameworks are basically single page applications. So, in a single page application, for example, we want to start from http://mondaylab.com Jump to http://mondaylab.com/list , how to jump?

This is about the routing problem of a single page application. We need to be on webpack config. JS is configured as follows:

module.exports = {
	devServer: {
        //The first way
		historyApiFallback: true,
        /*Equivalent to
        historyApiFallback: {
            rewrites: [{
                from: /\.*\/,
                to: '/list.html/'
            }]
        }
        */
        
        /*The second way
        historyApiFallback: {
            rewrites: [{
                from: /abc.html/,
                //Convert when accessing ABC HTML, list The content of HTML is displayed
                to: '/list.html/'
            }]
        }
        */
        
        /*The third way: more flexible
        historyApiFallback: {
            rewrites: [{
            	//Indicates that when replacing a page, a function is used in the form of function, combined with some parameters of context
            	//Do some js logic and put it in it to decide where it will jump in the end
                from: /^\/(libs)\/.*$/,
                to: function(context){
                	return '/bower_components' + context.match[0];
                }
            }]
        }
        */
	}
}

It is worth noting that historyApiFallback can only be used in the development environment. If you are in the online environment, you need to ask the back-end partners to go to nginx or apache, follow some configurations of webpackDevServer, and make the same configuration on the back-end server. After configuration, the front-end can use the corresponding route.

🚖 5, Configuration of ESLint in Webpack

1. What is eslint

In our daily team development, everyone writes all kinds of code. For example, some people like to add a semicolon after the code, while others don't. Indirectly, this can easily lead to poor maintainability of our project. Therefore, we have introduced the content of ESLint to restrict the code specification and improve the maintainability and scalability of the project.

How is ESLint configured in Webpack?

2. How to install ESLint

Step 1: install the ESLint tool. The specific commands are as follows:

npm install eslint --save-dev

Step 2: constrain our code. We need to create a new configuration file to configure our ESLint specification. The specific commands are as follows:

npx eslint --init
> Use a popular style guide Use a common code detection template
> Airbnb
> Do you use React Fill in according to your own needs y perhaps n
> Javascript
> Would you like to install them now with npm? Y

Step 3: use eslint to check the code specification. The specific codes are as follows:

npx eslint src

The above code means to use eslint to detect the code specification in the src directory.

3. Why should ESLint be configured in webpack

In the above, we learned that each development partner can use the command line to detect their own code specifications, but if we have to run such commands every time we write the code, we can see whether our code is reasonable or not. Will it be a little troublesome. At the same time, we can't guarantee that everyone's eslint code specification settings are the same.

Therefore, we can configure it in webpack to solve the above problems. The specific steps are as follows:

Step 1: install eslint loader. The specific command line is as follows:

npm install eslint-loader --save-dev

Step 2: configure webpack config. js . The specific codes are as follows:

module.exports = {
    devServer: {
      /*When we run webpack for packaging,
      Once there is a specification problem with the code,
      webpack An error reporting layer will pop up on the browser to prompt us*/
      overlay: true  
    },
    module: {
        rules: [{ 
			test: /\.js$/, 
			exclude: /node_modules/, 
			use: ['babel-loader', 'eslint-loader']
		}]
    }
}

After understanding the basic configuration, let's learn about some other configurations of eslint loader. The specific codes are as follows:

module.exports = {
    module: {
        rules: [{ 
			test: /\.js$/, 
			exclude: /node_modules/, 
			use: ['babel-loader', {
                loader: 'eslint-loader',
                options: {
                    //If there are some simple problems in the code, eslint loader will help us fix them automatically
                    fix: true,
                    //Reduce the loss of project performance caused by eslint in the packaging process
                    cache: true
                },
                //Force eslint loader to execute first
                fore: 'pre'
            }]
		}]
    }
}

🏎️ 6, Webpack performance optimization

Careful partners may have found that the packaging speed of Webpack may be a little slow sometimes. Indirectly, it will waste a lot of time we shouldn't waste. So, let's talk about several ways to improve the packaging speed of Webpack.

1. Keep up with the iteration of Technology (node, NPM, Yan)

If we want to improve the packaging speed of webpack, we can upgrade the version of webpack, or upgrade the version of node, npm manager or yarn.

So why can upgrading these tools improve the packaging speed of webpack.

Let's think about it. When we update each version of webpack, we will certainly do a lot of internal version optimization. Therefore, when we update the version of webpack, we will certainly improve the speed. The same is true for node, npm and yarn updates.

Just imagine, if you don't upgrade, what's the significance of upgrading? Right.

2. Apply Loader on as few modules as possible

In general, the libraries of third-party modules have been packaged and compiled, so we need to update node when introducing loader for compilation_ Module files are ignored, or only a loader is used in a folder to increase our packaging speed. We can go to webpack config. JS. The specific code is as follows:

module.exports = {
    module: {
        rules: [{ 
			test: /\.js$/, 
			exclude: /node_modules/,
            //Or use the following method - > include
			//include: path.resolve(__dirname, '../src'),
			use: [{
				loader: 'babel-loader'
			}]
		}]
    }
}

Of course, according to the above idea, other loader s also have their corresponding precautions, which will not be described in detail here.

3. Rational use of plug-ins

Plug-ins should be used reasonably. Don't use those redundant and meaningless plug-ins. At the same time, we should also choose those plug-ins with better performance and officially recognized. In this way, we can effectively improve the packaging speed of webpack.

4. Reasonable configuration of resolve parameters

(1) Common configuration

Sometimes, we want to make some custom configurations for the imported files. What should we do? The specific configuration is as follows:

module.exports = {
	resolve: {
		
		extensions: ['js', 'jsx'],
		/**
		 * 1.When you only import one directory, such as import child from '/ child/child' ,
		 * At this time, webpack doesn't know which file we want to import,
		 * Then it will look for the index file first. If it cannot be found, it will continue to look for the child file
		 */
		mainFiles: ['index', 'child'],
		/**
		 * alias, As the name suggests, it is an alias.
		 * For example, you want to change the import method of a file to your own name
		 * import Child from './Child' -> import Child from 'monday'
		 */
		alias: {
			monday: path.resolve(__dirname, '../src/Child'),
		}
	},
}

Next, the above parameters will be explained in detail.

(2) Parameter explanation

1)extensions

  • For example, when import child from 'is introduced/ When "child / child", I'll look for it first/ child/child.js' file, can't find it, find it again '/ child/child.jsx 'file.
  • css and picture files are generally not configured, because there may be a large number of css and pictures, and corresponding searches will be performed many times. Indirectly, I wanted to improve performance, but it turned out to be a waste of performance.
  • Therefore, resource files such as css and jpg should be explicitly introduced; For logical files such as js and jsx, you can configure them in extesions for explicit introduction.

2)mainFiles

When you only import one directory, such as import child from '/ Child / child ', at this time, webpack doesn't know which file we want to import, so it will look for the index file first. If it can't find it, it will continue to look for the child file.

3)alias

  • Alias, as the name suggests, is an alias. For example, you want to change the import method of a file to your own name → import child from '/ Child' -> import Child from 'monday' .
  • It is often used in the case of multi-level directories.

5. Use DllPlugin to improve packing speed

Sometimes, we want to introduce third-party modules that are only analyzed when packaging for the first time, but not when packaging later. What should I do?

Step 1: create a new webpack dll. JS file and configure it. The specific codes are as follows:

const path = require('path');
const webpack = require('webpack');

module.exports = {
    mode: 'production',
    entry: {
        //Fill in the name of the third party library we want to package separately here
        vendors: ['react', 'react-dom', 'lodash']
    },
    output: {
        filename: '[name].dll.js',
        path: path.resolve(__dirname, '../dll'),
        /*Use the library to store all the code in the third-party module,
        Exposed through global variables*/
        library: '[name]'
    },
    plugins: [
        /*After exposure, analyze the exposed module code with the help of DllPlugin plug-in, and finally generate manifest JSON file */
        new webpack.DllPlugin({
            //Perform DllPlugin analysis (file mapping) on the generated vendor library
            name: '[name]',
            // The path put by the analysis result, combined with the global variables after analysis, is in common JS
            path: path.resolve(__dirname, '../dll/[name].manifest.json')
        })
    ]
        
}

Step 2: install the add asset HTML webpack plugin. The specific commands are as follows:

npm install add-asset-html-webpack-plugin --save

Step 3: configure webpack common. JS file. The specific configuration is as follows:

const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
const webpack = require('webpack');

module.exports = {
    entry: {
        main: './src/index.js',
        plugins: [
            new HtmlWebpackPlugin({
                template: 'src/index.html'
            }), 
             /**
             * It refers to the index to be generated into the HTML webpack plugin plug-in Add some content to HTML
             */
            new AddAssetHtmlWebpackPlugin({
                filepath: path.resolve(__dirname, '../dll/vendors.dll.js')
            }),
            /**
             * 1.Using the DllReferencePlugin plug-in,
             * This plug-in will go to '/ dll/vendors.manifest.json 'to find the mapping relationship of the third-party module,
             * If the mapping relationship can be found, then webpack will know that there is no need to package this third-party module,
             * Directly from vendors dll. JS can be used
             * 2.If it is found and no longer in the mapping relationship, it will go to the node again_ Modules
             * 
             */
            new webpack.DllReferencePlugin({
                manifest: path.resolve(__dirname, '../dll/vendors.manifest.json')
            })
        ]
    }
}

Step 4: configure package JSON file. The specific codes are as follows:

{
	"scripts": {
		"build:dll": "webpack --config ./build/webpack.dll.js"
	}
}

Package the third-party library by running npm run build:dll command.

6. Control package file size

When we do project packaging, we should make the generated files as small as possible. Sometimes when we are writing code, we often introduce some useless modules into the page, that is, some modules we use.

At this time, if you do not configure tree shaking, it will easily lead to a large amount of redundant code during packaging. These redundant codes, indirectly, will drag down the packaging speed of our webpack.

Therefore, we should control the file size when packaging. Refer to the following steps:

  • Configure tree shaking;
  • Split a large file into multiple small files through SplittingChunk.

7. Multi process packaging

Webpack runs through node, so its packaging process is single threaded. Sometimes, we can also use the multi process in node to help us improve the packaging speed of webpack.

Common tools include thread loader, parallel webpack, happypack and other tools. You can find relevant information according to your own needs, select the most suitable tool for your project and package it. This section will not be explained in detail~

8. Rational use of sourceMap

Generally, the more detailed the sourceMap is, the slower the packaging will be. Therefore, when packaging, we should select the most appropriate sourceMap configuration according to the current development environment or production environment to generate our corresponding code debugging file.

In this way, on the one hand, we can ensure that even if we find errors in the code. On the other hand, you can also improve the packaging speed as much as possible.

9. Analyze the packing results with stats

When packaging the project, we can generate stats files of this packaging situation through commands. Then, we can analyze the packaging situation in this packaging process with the help of some online or local packaging analysis tools.

For example, analyze which module takes a long time to package and which module takes a short time to package and analyze, and optimize it according to the specific situation.

10. Development environment memory compilation

Using webpackDevServer to compile, it will not put the packaged files in the dist directory, but put them in the memory of our computer.

11. Elimination of useless plug-ins in development environment

For example, we do not need to compress the code in the development environment. Therefore, we should not configure the corresponding compression plug-ins in the development environment, but only in the production environment. In this way, you can reduce some unnecessary packaging time.

🏍️ 7, Multi page packaging configuration

Generally speaking, whenever we do packaging, we basically package single page applications. What is a single page, that is, there is only one index HTML file. At present, the mainstream frameworks, such as vue and react, are single page applications. However, some older frameworks, such as jquery and zepto, may require multi page application packaging.

Therefore, following this topic, let's talk about how to package and configure multi page applications in webpack.

We're on webpack common. JS. The specific code is as follows:

const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: {
		//Import multiple entry files
		main: './src/index.js',
		list: './src/list.js'
	},
    plugins: [
        new HtmlWebpackPlugin({
		template: 'src/index.html',
		filename: 'index.html',
		//chunk indicates which of these html files to import
		chunks: ['runtime', 'vendors', 'main']
        }),
        new HtmlWebpackPlugin({
            template: 'src/index.html',
            filename: 'list.html',
            chunks: ['runtime', 'vendors', 'list']
        })
    ]
}

Through the above code, we can know that the configuration of entry and plugins is added to add multiple entry pages, so as to achieve the effect of multi page application configuration.

🛵 8, Conclusion

Through the above explanation, I believe you have a certain understanding of the actual configuration of webpack in some scenarios. The above content is also relatively simple. You can broaden your knowledge according to the corresponding knowledge points, so as to better apply it to practical projects.

Here, the actual case configuration of webpack is over! I hope it will help you~

If the article is wrong or doesn't understand, please leave a message in the comment area 💬

🐣 One More Thing

(: recommended in previous periods)

(: Fan Wai Pian)

  • Pay attention to the official account of Monday's research room. First, we will focus on quality articles.
  • If this article is useful to you, remember to leave a footprint jio before you go~
  • The above is the whole content of this article! See you next time! 👋👋👋

Keywords: Javascript Front-end JSON Webpack

Added by p.utsav on Sat, 15 Jan 2022 06:23:01 +0200