This is the third day of my participation in the August More text Challenge. For details, see: August More Text Challenge

🤾 ♀ ️ preface

In the last article, we covered some of the basic features of WebPack, but it’s not enough. So, in today’s article, you’ll be introduced to advanced webPack features, including, but not limited to, differentiated packaging between dev and Prod environments, and techniques for code splitting projects using WebPack.

Without further ado, let’s begin today’s study ~🎳

🏓 1, Tree Shaking

1. Introduction

Let’s say we now have a requirement to write a program that adds and subtracts two numbers. Now, let’s implement this function. The specific code is as follows:

export const add = (a, b) = > {
    console.log(a + b);
}

export const minus = (a, b) = > {
    console.log(a - b);
}

Copy the code

Next, we import it in the entry file. The specific code is as follows:

import { add } from './math.js';

add(1.2);
Copy the code

In this state, we use the NPX webpack command to package the project. To view the code for the packaged file:

/ * * * / "./src/math.js":
/ *! * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/math.js ***! \ * * * * * * * * * * * * * * * * * * * * * /
/ *! exports provided: add, minus */
/ * * * / (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add".function() { return add; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "minus".function() { return minus; });
const add = (a, b) = > {
  console.log(a + b);
};
const minus = (a, b) = > {
  console.log(a - b);
};

/ * * * / })
Copy the code

As you can see, we only introduced addition in the entry file, because we only want to use addition right now, and we don’t need subtraction yet. But when you package the file, you pack the subtraction with it. This invisible, more or less to us to add a lot of trouble.

Therefore, we need to introduce Tree Shaking in WebPack to solve this problem.

2. Configure Tree Shaking

First we need to configure it in webpack.mon.js. If you are in Development mode, the default is not to own Tree Shaking. Therefore, you need to perform the following configuration. The specific code is as follows:

// Core module of node
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
	mode:'development'.optimization: {
		usedExports: true}}Copy the code

In a development environment, we need to add the Optimization module to enable Tree Shaking for the project.


Next, let’s go ahead and configure package.json. Under this file, we need to add the following code:

{
	"sideEffects": false
}
Copy the code

SideEffects: What does false mean? When set to false, Tree Shaking is enabled for all ES modules.

It is important to note that Tree Shakig only supports the introduction of ES Module types. It does not support the introduction of types such as commonJS. This is because the underlying implementation of ES Module is static, whereas the commonJS implementation is dynamic.


In another case, if you want to leave Tree Shaking off some modules, you can configure sideEffects as follows. The specific code is as follows:

{
	"sideEffects": [
		"*.css"."@babel/poly-fill"]}Copy the code

The above code means that tree-shaking is not enabled for all CSS files and @babel/poly-fill.


Let’s take a look at how this configuration works in a development environment. The specific code is as follows:

/ * * * / "./src/math.js":
/ *! * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/math.js ***! \ * * * * * * * * * * * * * * * * * * * * * /
/ *! exports provided: add, minus */
/ *! exports used: add */
/ * * * / (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a".function() { return add; });
/* unused harmony export minus */
const add = (a, b) = > {
  console.log(a + b);
};
const minus = (a, b) = > {
  console.log(a - b);
};

/ * * * / })

/ * * * * * * / });
//# sourceMappingURL=main.js.map
Copy the code

As you can see, in development mode, the subtraction function still exists, just multiple /*! Exports used: add */

Why is that? Because in development mode, WebPack did not delete this area because it was afraid that if another module was introduced, it would easily cause an error.


But when mode is in a production environment, Tree Shaking can be very useful. Let’s look at the packaged results after we modify the mode. The specific code is as follows:

function (e, n, r) { "use strict"; r.r(n); var t, o; t = 1, o = 2.console.log(t + o) }
Copy the code

In the production environment, there is only one line after packaging, and the small series takes the final part out.

As you can see, when in production, the packaged results only show the addition function. And we’re not using subtraction, so we’re not going to wrap it up at this point.

With that in mind, let’s look at the separate packaging of WebPack in development and Production mode.

🏸 ii. Differentiated packaging of Development and Prodiction modes

1. Project packaging structure

Typically, our projects will have three webPack configuration files. One is webpack.mon.js, one is webpack.dev.js, and the other is webpack.prod.js. The first file is used to hold the configuration for both development and production environments, the second file is used to hold the configuration for development, and the third file is used to hold the configuration for production.

Let’s look at the code for these three configuration files.

2. Configure webpack.mon.js

If we don’t write the common file, the code for dev and prod will overlap, so we’ll pull out the same parts. The specific code is as follows:

// Node core module
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
    // Put the entry file, specify how to package
	entry: {main: './src/index.js'
	},
    module: {rules: [{test: /\.m? js$/.//exclude = exclude = exclude If the js file is in the node_modules folder, then we exclude it
			// Since node_module usually comes from a third-party library, it already handles this part of the work automatically, so there is no need to repeat the operation
			exclude: /node_modules/,
			use: {
			  loader: "babel-loader",}}, {test:/\.(jpg|png|gif)$/,
			use:{
				loader:'file-loader'.options: {
					/ / placeholder placeholder
					name: '[name]_[hash].[ext]'.outputPath: 'images/'.limit: 10240}}}, {test:/\.scss$/,
			use:[
				'style-loader', 
				{
					loader: 'css-loader'.options: {
						// Postcss-loader and sass-loader must be used first
						importLoaders: 2.modules: true}},'sass-loader'.'postcss-loader'] {},test:/\.css$/,
			use:[
				'style-loader'.'css-loader'.'postcss-loader'] {},test: /\.(eot|ttf|svg)$/,
			use: {
				loader: 'file-loader',}}},plugins: [new HtmlWebpackPlugin({
		// Specify which template to reference
		template: 'src/index.html'
	}),new CleanWebpackPlugin(['dist']]),// Output, indicating how WebPack should output
	output: {
		publicPath: '/'.// Use [] to generate multiple files
		filename: '[name].js'.// Which file to put the packaged file under
		path: path.resolve(__dirname, 'dist')}}Copy the code

3. Development environment webpack.dev.js

With the common code out of the way, let’s now write the webpack.dev.js file. The specific code is as follows:

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

const devConfig = {
	mode:'production'.devtool: 'cheap-module-eval-source-map'.devServer: {
		contentBase: './dist'.// When NPM run start is finished, it automatically opens the browser for us
		open: true.port: 8080.// Let our webpackDevServer enable hotModuleReplacement like this
		hot: true.// Do not let the browser refresh automatically even if the HMR does not take effect
		hotOnly: true
	},
	plugins: [
        // Update the hot module
		new webpack.HotModuleReplacementPlugin()
	],
	optimization: {
		usedExports: true}}module.exports = merge(commonConfig, devConfig)
Copy the code

4. Production environment webpack.prod.js

Moving on, let’s write the extracted PROD code. The specific code is as follows:

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

const prodConfig = {
	mode:'development'.devtool: 'cheap-module-source-map'
}

module.exports = merge(commonConfig, prodConfig)
Copy the code

By comparing the above three files, you can see that this kind of code separation makes our project structure much clearer.

5. Run the project package.json

With the above configuration complete, we now need to think about whether we should also distinguish between the commands that run in the development and production environments if we want to run different configurations. Therefore, we configure the package.json file as follows:

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

With this configuration, we can then run the project with the commands NPM run dev and NPM run build to distinguish between a development and production environment.


At the same time, if we want to see more intuitive error messages on the console, we can run with WebPack instead of webpack-dev-server in the development environment. The specific code is as follows:

{
    "scripts": {
    	"dev-server": "webpack --config webpac.dev.js"."dev": "webpack-dev-server --config webpack.dev.js"."build": "webpack --config webpack.prod.js"}}Copy the code

This way, we can run the project using WebPack via NPM run dev-build.

⚽ Webpack and Code Splitting, SplitChunksPlugin

1. Code Splitting

In some cases, we may have a business logic with tens of thousands of lines of code, and then all of them are packaged and thrown into the main.js file. Such a large file will make the whole project load very slowly. Therefore, we need Code Splitting to solve this problem.

We do this configuration in webpack.mon.js. The specific code is as follows:

module.exports = {
	optimization: {
		splitChunks: {
			chunks: 'all'}}}Copy the code

From the above code, we can see that the effect of code segmentation is achieved by using splitChunks in Optimization.

So what does WebPack want to do with this configuration?

In fact, using splitChunks helps webPack automatically package and generate a new file when it encounters a common class library, and then split the rest of the business logic into another file.

It’s important to note that WebPack can help with code splitting whether the common libraries are loaded synchronously or asynchronously.

2. Introduction -SplitChunksPlugin

We talked about webPack code splitting above, so in fact, webPack code splitting, the underlying principle of the implementation of the splitChunksPlugin plug-in. Let’s look at the plugin.

Before we used SplitChunksPlugin, if we introduced a library asynchronously, WebPack would package it with names like 0.js, 1.js, and…… .

Now we want WebPack to give our third-party libraries a custom name when we do code splitting. What about that?

3. Implementation mode -SplitChunksPlugin

First we add the webpackChunkName configuration to the front of the imported library. The specific code is as follows:

function getComponent() {
    return import(/*webpackChunkName:"lodash"*/'lodash').then(({ default: _}) = > {
        var element = document.createElement('div');
        element.innerHTML = _.join(['Monday'.'Tuesday'].'_');
        return element;
    })
}

getComponent().then(element= > {
    document.body.appendChild(element);
})
Copy the code

/*webpackChunkName:”lodash”*/


In the first step of the configuration knowledge above, we will install and use a dynamically introduced plug-in. The specific code is as follows:

Install plug-in:

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

in.babelrcIntroduction:

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

configurationwebpack.common.js

module.exports = {
	optimization: {
		splitChunks: {
			chunks: 'all'.cacheGroups: {
				vendors: false.default: false}}}},Copy the code

4. SplitChunksPlugin Configures parameters

Next, let’s look at some common configurations for SplitChunksPlugin. The specific code is as follows:

module.exports = {
	optimization: {
		splitChunks: {
		  /* When async is used, only asynchronous code is partitioned; When all is used, code segmentation is performed for both synchronous and asynchronous code. When initial is used, code segmentation */ is performed on the synchronized code
		  chunks: 'all'.// If the size is greater than 30KB, do code segmentation
		  minSize: 30000.// Indicates the maximum size of the file after code splitting. If this is exceeded, the split will continue; There are some files that will be useless if you can't split them
		  maxSize: 0.minRemainingSize: 0.// At least two blocks will be used before extracting
		  minChunks: 2.// indicates the number of modules to be loaded simultaneously. The maximum number is 5
          /* If we introduce 10 libraries, we will do 10 code splits. If this parameter is set to 5, webPack will generate 5 js files for the first 10 libraries. After that, we will not split the code any more, and all the files will be thrown into one file */
		  maxAsyncRequests: 5.// The entry file can be split into a maximum of 3 js files. If there are more than 3 js files, the entry file will not be split
		  maxInitialRequests: 3.// Intermediate symbol for file generation
		  automaticNameDelimiter: '~'.// Make the file names in defaultVendors and Default valid
		  name: true.enforceSizeThreshold: 50000.// cacheGroups when packaging synchronization code
		  cacheGroups: {
			defaultVendors: {
			  // Check the library you imported to see if it is under node_module
			  test: /[\\/]node_modules[\\/]/,
			  priority: -10.// After determining that it is under node_modules, package it up and name it filents.js
			  filename: 'vendors.js'
			},
            // Do code segmentation for non-third-party library code
			default: {
				priority: -20.reuseExistingChunk: true.filename: 'common.js'}},}}}Copy the code

🏐 4. Package analysis, Preloading, Prefetching

1. Package and analyze

Packaging analysis means that when we use WebPack to package code, we can use some packaging analysis tools to analyze the generated files and see if they are properly packaged. So how do you do package analysis?

We need to use a third-party repository on Github, so poke this link to access ~

With the contents of the library documented, the next step is to configure package.json. The specific code is as follows:

{
    "scripts": {
        "dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js"."dev": "webpack-dev-server --config ./build/webpack.dev.js"."build": "webpack --config ./build/webpack.prod.js"}}Copy the code

From the above code, we can analyze: –config –profile –json > stats.json –config –profile –json > stats.json –config –config –profile –json > stats.json The stats. Json file format is a JSON format.


After generating the stats.json file, we can put it into the packaging tool for analysis. You can locate the bundle-Analysis in the official document, which provides visualization tools such as Webpack-Chart and Webpack-Visualizer for us to use. You can select the corresponding tools according to your personal needs. Analyze stats. Json file.

2. Preloading, Prefetching

(1) Introduction

In fact, when we configure splitChunks, the default value inside is async. That is, if we don’t configure it, WebPack will only code split asynchronous code by default. Why does WebPack do this?

Webpack thinks it’s good to pack the synchronous code in a single file, but it also wants us to write more asynchronous load code to give our site a real performance boost.

Now, let’s talk about a very common scene in life. If we log on to The website of Zhihu now, we have not logged in when we first entered. What we want now is to click on the login button and the login mode box will show up, instead of clicking on it and waiting for it to load, which makes the page load a lot faster.

So how do you deal with this? This comes to preloading and prefetching in WebPack.

(2) Preloading and prefetching

Let’s say we now want to introduce a click.js file, so we can do this. The specific code is as follows:

document.addEventListener('click'.() = > {
    // When the main js files have been loaded, it will load the./click.js files secretly when the bandwidth is free
    import(/* webpackPrefetch: true */ './click.js').then(({default: func}) = >{ func(); })});Copy the code

/* webpackPrefetch: true */ before the imported file, i.e./click.js. When the main js files have been loaded, webpack will secretly load the./click.js files during the time when the network bandwidth is idle.

Prefetch: webpackPrefetch: webpackPrefetch: webpackPrefetch: webpackPrefetch: webpackPrefetch: webpackPrefetch: webpackPrefetch: webpackPrefetch Preload is loaded at the same time as the main file, not after the main file is loaded. In general, we use prefetch, and then load the rest of the files we want until the main file is finished. This logic and optimization of the page is perfect.

To sum up, we used to package libraries like jQuery and Lodash so that we could load them on the first visit, and then on the second visit we could use caching to speed up access. But it only speeds up the second visit, and what we want is for WebPack to load the page as fast as possible on the first visit.

So we ended up using preload and prefetch to solve this problem.

🏏 5. Code segmentation of CSS files

1. CSS file code segmentation

So we’ve talked about how to code split a JS file, and now we’re going to talk about how to code split a CSS file. For code splitting CSS files, we refer to a plugin mentioned in the official documentation: MiniCssExtractPlugin. Let’s take a look at how this plug-in works.

Step 1: Install the plug-in. The command is as follows:

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

Step 2: Use in development and online environments. Let’s open the webpack.mon.js file, introduce the plug-in, and use it. The specific code is as follows:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
	entry: {main: './src/index.js'
	},
    module: {rules: [{test:/\.scss$/,
			use:[
				MiniCssExtractPlugin.loader, 
				{
					loader: 'css-loader'.options: {
						// Postcss-loader and sass-loader must be used first
						importLoaders: 2.modules: true}},'sass-loader'.'postcss-loader'] {},test:/\.css$/,
			use:[
				MiniCssExtractPlugin.loader,
				'css-loader'.'postcss-loader']]}},plugins: [
		new MiniCssExtractPlugin({
			// If the file is referenced directly, go to filename
			filename: '[name].css'.// If the file is indirectly referenced, go to chunkFilename
			chunkFilename: '[name].chunk.js'})].optimization: {
		/ / use treeShaking
		usedExports: true.splitChunks: {
			chunks: 'all'.cacheGroups: {
				vendors: false.default: false}}}}Copy the code

Step 3: Configure the package.json file. The specific code is as follows:

{
  "sideEffects": [
    // treeShaking is not enabled for CSS files
    "*.css"]}Copy the code

2. Compress the CSS file

For the packaged CSS file, the size is relatively large, so we need to compress the file size. How do you deal with that?

** Step 1: ** Install the plug-in. The command is as follows:

npm install optimize-css-assets-webpack-plugin -D
Copy the code

Step 2: Use in development and online environments. Let’s open the webpack.mon.js file, introduce the plug-in, and use it. The specific code is as follows:

const CssMinimizerPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {
    optimization: {
        minimizer: [new OptimizeCSSAssetsPlugin({})]
    }
}
Copy the code

3. Merge and package the CSS file

Sometimes, we may have multiple entry files, and each entry file has several CSS files corresponding to it. So what do we do when we want to pack all of our CSS files into one file?

We need to configure it in webpack.mon.js. The code is as follows:

module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: "styles".test: /\.css$/,
          chunks: "all".enforce: true},},},},},}Copy the code

From the code above, we can see that we need to configure an additional styles cachegroup in splitChunks to package all CSS files into a folder named Styles.

🏑 Vi. Webpack and browser caching

1. Configure the browser cache

When we first visit the site, the first load always requires the various files to be loaded from scratch, and assuming our code hasn’t been updated, we want to be able to pull the cache directly from the browser when we reload instead of reloading. Wait until our code is updated, then reload the page. So how do you deal with this?

Step 1: Configure the development environment webpack.dev.js file. The specific code is as follows:

const devConfig = {
    output: {
		filename: '[name].js'.chunkFilename: '[name].chunk.js',}}Copy the code

Step 2: Configure the production environment webpack.prod.js file. The specific code is as follows:

const prodConfig = {
    output: {
		filename: '[name].[contenthash].js'.chunkFilename: '[name].[contenthash].js',}}Copy the code

The above code is designed to solve the problem of adding a hash value to the output file when the environment is online. At this point, if our code changes, WebPack will generate a new hash and the page will be updated. If our code doesn’t change, then the hash will be the same and the page will pull the memory information from the browser and load it.

2. Resolve the problem of the earlier version

This will not work if it happens in some of the lower versions of WebPack. So we need to do a configuration that is compatible with the earlier version issues. We use webpack.mon.js to configure the configuration. The code is as follows:

module.exports = {
    optimazation: {
       	runtimeChunk: {
        	name: 'runtime'}}}Copy the code

⚾ 7. The role of Shimming

1. The Shimming gasket

Moving on, now let’s look at the concept of shimming in Webpack.

In the webPack packaging process, we often have to do some code or packaging compatibility.

For example, for two JS files, modules are independent of each other, and there is no degree of coupling between them. Suppose we now have two files with the following code:

jquery.ui.js:

export function ui(){$('body').css('background'.'red');
}
Copy the code

index.js:

import _ from 'lodash';
import $ from 'jquery';

import { ui } from './jquery.ui';

ui();

const dom = $('<div>');
dom.html(_.join(['Mondady'.'Tuesday']), '~');
$('body').append(dom);

Copy the code

As you can see, we now want to introduce $in the index.ui.js file, but in this file, it does not introduce the jquery library. So, if you just run it like this, you’re going to get an error.

So, if we want to solve this problem, how do we configure it?

Next we will configure webpack.mon.js. The specific code is as follows:

const webpack = require('webpack');

module.exports = {
    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery'.//'_': 'lodash',
            //_join: ['lodash', 'join']}})]Copy the code

So, this indirectly means, take something and put the $on it, and that’s what we call the shimming concept in the title.

2. This point

For files in a project, this points to the module itself, not to the global. What if we want all js files in the project to point to global?

Step 1: Install the Loader. The command is as follows:

npm install imports-loader --save-dev
Copy the code

Step 2: Configure webpack.mon.js. The specific code is as follows:

module.exports = {
    module: {
        rules: [{
			test: /\.m? js$/,
			exclude: /node_modules/,
			use: [{
				loader: "babel-loader"}, {loader: 'imports-loader? this=>window'}}}}]]Copy the code

After configuring the imports- Loader, WebPack indicates that it will point this in the JS file to all points in the Window global.

🎖️

In today’s article, we’ve learned how to use Tree Shaking to optimize the packaging size of code. We’ve also learned how to differentiate packaging in Dev and Prod environments, and how to distinguish between the same configuration and different configurations in each mode.

In addition, we also learned to use WebPack to code split JS files and CSS files. And use WebPack to package and preload the code.

Finally, we learned about How WebPack turns on the browser cache and how Shimming works.

So much for webPack’s basic and advanced features! Hope to help you ~

If you have any questions or articles are wrong, please leave a message in the comments section ~💬

🐣 Easter Eggs One More Thing

(: Recommended in the past

👉 WebPack Basics series

👉 Vuejs Basic knowledge series

(: External chapter

🙃 pay attention to the public Monday research office, the first time to pay attention to quality articles, more selected columns for you to unlock ~

🙃 If this article was useful to you, be sure to leave a footprint before you go

🙃 and that’s all for this article! See you next time! 👋 👋 👋