1 Code Fragments
Code splitting is a technology unique to Webpack, which can split the code in a specific form. Instead of loading all at once, code splitting can be loaded on demand, which can effectively reduce the size of resources loaded on the first screen.
1.1 CommonsChunkPlugin
CommonsChunkPlugin is a built-in plugin for Webpack4 that extracts the common parts of multiple chunks. Thus reduce the repeated packaging of modules in the development process and improve the development speed. The overall size of the resource is reduced and the client cache can be effectively utilized.
CommonsChunkPlugin can be configured with the following attributes.
name
Will:chunks
Attribute correspondingsource chunk
The public module is extracted fromname
, if not specifiedchunks
, will be extracted by defaultentry chunks
Public modules inchunks
: specifysource chunk
, that is, from whatchunk
To find public modules, omitting this option defaults toentry chunks
filename
: Indicates the extracted resource file name, which can be dynamically generated in template languageminChunks
: can be a number, function, orInfinity
1.1.1 Non-plug-in packaging
The root directory contains package.json, webpack.config.js, SRC, etc. The SRC directory contains foo.js, bar.js, utils.js.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"webpack": "^ 3.10.0"
},
"dependencies": {
"jquery": "^ 3.2.1." "}}// webpack.config.js
module.exports = {
entry: {
foo: './src/foo.js'.bar: './src/bar.js'
},
output: {
filename: './dist/[name].js'}}// src/bar.js
import jquery from 'jquery'
import { log } from './utils.js'
console.log(jquery, log, 'bar')
// src/foo.js
import jquery from 'jquery'
import { log } from './utils.js'
console.log(jquery, log, 'foo')
// src/utils.js
export function log(){
console.log('log')}Copy the code
Running the package will generate bar.js and foo.js in the root dist folder, where jquery and utils are packaged into these two files.
Finally, you need to add script tags to the page to introduce foo.js and bar.js.
// src/index.html
<html lang="zh-CN">... <body><script src="./bar.js"></script>
<script src="./foo.js"></script>
</body>
</html>
Copy the code
1.1.2 Extract common code
Modify webpack.config.js and add CommonsChunkPlugin to extract common modules.
// webpack.config.js
const webpack = require("webpack");
module.exports = {
entry: {
foo: './src/foo.js'.bar: './src/bar.js'
},
output: {
filename: './dist/[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'.filename: './dist/[name].js']}}),Copy the code
The third-party modules jquery in foo.js and bar.js as follows, as well as the public modules utils and webpack running files in the project, have been packed into vendor.js, and the size of foo.js and bar.js has been significantly reduced.
Vendor.js should be introduced before any other JS on the page.
// dist/index.js
<html lang="zh-CN">... <body><script src="./vendor.js"></script>
<script src="./bar.js"></script>
<script src="./foo.js"></script>
</body>
</html>
Copy the code
1.1.3 Extract the runtime
When extracting a common module using a plug-in, the extracted resource contains not only the module code but also the WebPack runtime. The WebPack runtime refers to the code that initializes the environment, such as creating module cache objects, declaring module load functions, and so on.
The first CommonsChunkPlugin instance below extracts the third-party jquery module, local utils module, and Webpack runtime files from foo.js and bar.js into vendor.
Then each CommonsChunkPlugin instance extracts the runtime file from vendor to Runtime. The final vendor includes jquery and utils, and runtime contains the WebPack runtime file.
Note that the CommonsChunkPlugin for Runtime must appear at the end of plugins, otherwise WebPack will not extract the module properly.
// webpack.config.js
const webpack = require("webpack");
module.exports = {
entry: {
foo: './src/foo.js'.bar: './src/bar.js'
},
output: {
filename: './dist/[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'.filename: './dist/[name].js'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'.filename: './dist/[name].js'.chunks: ['vendor']})]}Copy the code
Running the package will generate files such as Runtime.js and rendor.js in the dist directory.
These plugins are equivalent to the following.
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor'.'runtime'].filename: './dist/[name].js'})]Copy the code
When minChunks are set to N, it means that a module can be extracted only when n chunks are referenced simultaneously. The CommonsChunkPlugin defaults to extracting a module as long as it is referenced by two entry chunks, i.e. minChunk defaults to 2.
If set to Infinity, the extraction threshold is infinitely high, that is, all modules will not be extracted.
Infinity is used to make webPack extract only specific modules, and to generate a file runtime that contains only the WebPack runtime without any modules.
The following represents extracting the outbound files from the entry chunk, and then extracting the third-party modules and local utils modules from Foo and bar.
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'.filename: './dist/[name].js'.minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'.filename: './dist/[name].js'.chunks: ['foo'.'bar'"})"Copy the code
1.1.4 Extracting third-party modules and local modules
The first CommonsChunkPlugin instance has minChunks set to Infinity, which means that none of the modules will be extracted. In this case, the Name is set to Runtime and the WebPack runtime file can be extracted. However, since name is specified as vendor and vendor is declared in the entry entry, it means that only the module (jquery) corresponding to the Vendor array and the Webpack runtime file are extracted.
The next CommonsChunkPlugin instance extracts the WebPack runtime file from vendor, which only contains the third-party module jquery, and runtime which contains the runtime file.
The last CommonsChunkPlugin instance finally extracted the local modules from Foo and bar into utils.
// webpack.config.js
const webpack = require("webpack");
module.exports = {
entry: {
foo: "./src/foo.js".bar: "./src/bar.js".vendor: ["jquery"],},output: {
filename: "./dist/[name].js",},plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor".filename: "./dist/[name].js".minChunks: Infinity,}).new webpack.optimize.CommonsChunkPlugin({
name: "runtime".filename: "./dist/[name].js".chunks: ["vendor"],}).new webpack.optimize.CommonsChunkPlugin({
name: "utils".filename: "./dist/[name].js".chunks: ["foo"."bar"],}),]};Copy the code
Running the package will generate vendor.js, utils.js, and runtime.js in the dist directory.
These plugins are equivalent to the following.
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ["vendor"."runtime"].filename: "./dist/[name].js".minChunks: Infinity,}).new webpack.optimize.CommonsChunkPlugin({
name: "utils".filename: "./dist/[name].js".chunks: ["foo"."bar"],})]Copy the code
In the minChunks function of the first CommonsChunkPlugin instance, module.resource is the complete path containing the module name, count is the number of times the module is referenced, By traversing the entry file and its dependent modules, if the module is in node_modules, it will be extracted to vendor. In essence, this method allows vendor to keep only third-party modules.
// webpack.config.js
const webpack = require("webpack");
module.exports = {
entry: {
foo: "./src/foo.js".bar: "./src/bar.js",},output: {
filename: "./dist/[name].js",},plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor".filename: "./dist/[name].js".minChunks: function (module, count) {
return module.resource && module.resource.includes("node_modules"); }}),new webpack.optimize.CommonsChunkPlugin({
name: "runtime".filename: "./dist/[name].js".chunks: ["vendor"],}).new webpack.optimize.CommonsChunkPlugin({
name: "utils".filename: "./dist/[name].js".chunks: ["foo"."bar"],}),]};Copy the code
Each JS mode is referenced as follows.
<html lang="zh-CN">... <body><script src="./runtime.js"></script>
<script src="./utils.js"></script>
<script src="./vendor.js"></script>
<script src="./foo.js"></script>
<script src="./bar.js"></script>
</body>
</html>
Copy the code
1.1.5 Extracting third-party Modules from single-page Applications
Create a separate entry for the single-page app. The following vendor contains the third-party module Vue and webPack runtime files.
// webpack.config.js
const webpack = require("webpack");
module.exports = {
entry: {
main: "./src/main.js".vendor: ["vue"],},output: {
filename: "./dist/[name].js",},plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor".filename: "./dist/[name].js",})]};Copy the code
After packaging, main.js and Vendor.js are generated in the dist directory.
The method of referencing JS is as follows.
<html lang="zh-CN">... <body><script src="./vendor.js"></script>
<script src="./main.js"></script>
</body>
</html>
Copy the code
1.1.6 Loading Resources asynchronously
If the number of modules is too large or the resource volume is too large, some modules that are not used temporarily are loaded lazily. Make the first rendering of the page download resources as small as possible, subsequent modules wait until the appropriate time to trigger the load, this way is loaded on demand.
Webpack officially recommends using the import function to asynchronously load a module and return a Promise object.
The following root directories include webpack.config.js and SRC folders. SRC contains main.js and utils.js, and import is used to load utils.js asynchronously.
// src/main.js
setTimeout(() = > {
import(/* webpackChunkName: "utils-chunk" */ "./utils.js");
}, 2000);
console.log("main");
// src/utils.js
import jquery from "jquery";
console.log(jquery, "utils");
// webpack.config.js
module.exports = {
entry: "./src/main.js".output: {
filename: "./dist/[name].js".chunkFilename: "./dist/[name].js".publicPath: ".. /",}};Copy the code
Output. chunkFilename specifies the file name of asynchronous chunk. The template language is supported. Asynchronous chunk does not have a name by default, and its default value is [id].
WebpackChunkName /* webpackChunkName: “utils-chunk” */, and the chunk name is utils-chunk.
After packaging, main.js is used as the resource loaded on the first screen, which is referenced by script tag in the page, while the request path of indirect resource (utils-chunk.js) is specified by output.publicPath.
The page is referenced as follows.
<html lang="zh-CN">... <body><script src="./main.js"></script>
</body>
</html>
Copy the code
After 2s, a script tag is dynamically inserted inside the page head tag, which references utils-chunk.js.
1.2 optimization. SplitChunks
The CommonsChunkPlugin can extract common modules in many scenarios, but its limitations are obvious.
- A single
CommonsChunkPlugin
Instances can only be extracted individuallyvendor
, if you want to extract multiplevendor
Need to add moreCommonsChunkPlugin
Example, easy to cause configuration code duplication - multiple
CommonsChunkPlugin
There may be logical relationships between instances, and only a correct logical relationship can ensure that the extracted code is as expected, and some configurations are not easy to understand and cannot be done out of the box
Webpack4 removes CommonsChunkPlugin, improves CommonsChunkPlugin and redesigns and implements code fragmentation features to simplify the configuration of code fragmentation through the optimization.splitchunks attribute.
1.2.1 Extract common code
Update the Webpack version, replace CommonsChunkPlugin with splitChunks for packaging asynchronous resources, and adjust webpack.config.js.
// webpack.config.js
module.exports = {
entry: "./src/main.js".output: {
filename: "[name].js",},optimization: { splitChunks: { chunks: "all"}},mode: "none"};// package.json{..."devDependencies": {
"webpack": "4.29.4"."webpack-cli": "3.2.3"}}Copy the code
The result of the packaging run is as follows, where main.js and utils.js are packaged separately, and the third-party module jquery referenced in utils.js is packaged into vendors~utils-chunk.js.
After the page references main.js, vendors~utils-chunk.js and Utils-chunk. js are inserted inside the page head tag 2 seconds later, and the number of parallel requests is 2.
The CommonsChunkPlugin scenario does not extract the jquery module from utils, and if only one line of utils is modified, the client will have to re-download the whole utils-chunk.js. While splitChunks extract jquery modules, jquery will not change frequently and modify one line of utils code. The client only needs to re-download utis-chunk. js, which is less than 1 KiB in size. Vendor-utils-chunk. js also makes good use of the browser cache.
1.2.2 Extraction conditions
The CommonsChunkPlugin is more imperative in that it extracts specific modules through configuration items. The difference of splitChunks is that only part of the extraction conditions, such as module volume and module location, are set. When certain modules meet these conditions, they will be automatically extracted in a more declarative way. The default extraction conditions for splitChunks are as follows.
- After extraction of the
chunk
fromnode_modules
Directory, in thenode_modules
The module is generally a general module, more suitable to be extracted - After extraction of the
javascript chunk
Volume is greater than the30KB
.CSS chunk
Volume is greater than the50KB
Generally, the resource volume after extraction is too small, and the optimization effect is relatively general - During the on-demand loading process, the maximum value of resources requested by parallel requests is less than or equal to
5
- During the first load, the maximum number of resources in parallel requests must be smaller than or equal to
3
1.2.3 Extracting Multiple Asynchronous Resources
The root directory contains webpack.config.js and SRC folders, while SRC contains main.js, foo.js and bar.js.
// src/main.js
setTimeout(() = > {
import(/* webpackChunkName: "bar-chunk" */ "./bar.js");
import(/* webpackChunkName: "foo-chunk" */ "./foo.js");
}, 2000);
console.log("main");
// src/bar.js
import react from "react";
console.log(react, "bar");
// src/foo.js
import vue from "vue";
import react from "react";
console.log(vue, react, "foo");
Copy the code
After packaging is run, Webpack creates a code block containing vUE (vendor~foo-chunk), which Foo-chunk relies on, as well as a code block containing React (vendors~bar-chunk~foo-chunk), Foo-chunk and bar-chunk depend on this code block.
Vendors ~bar-chunk~foo-chunk.js and bar-chunk.js are loaded in parallel when import(‘./bar.js) is called. Vendor-chunk. js and foo-chunk.js are loaded in parallel when import(‘./foo.js) is called, and vendors~bar-chunk~foo-chunk.js are loaded instead of vendors~bar-chunk~foo-chunk.js, and are simply loaded from the cache.
1.3 splitChunks Configuration parameters
The default values for splitChunks given by Webpack are as follows.
optimization: {
splitChunks: {
chunks: 'async'.minSize: 30000.maxSize: 0.minChunks: 1.maxAsyncRequests: 5.maxInitialRequests: 3.automaticNameDelimiter: '~'.name: true.cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2.priority: -20.reuseExistingChunk: true}}}}Copy the code
1.3.1 chunks
SplitChunks can be configured in splitChunks mode, including async (the default value), INITIAL (the default value), and ALL. Async only extracts asynchronous chunks, initial only extracts synchronous chunks, and ALL enables both modes.
The root directory contains webpack.config.js and SRC, and SRC contains main.js, bar.js, and foo.js.
// src/main.js
import jquery from 'jquery'
setTimeout(() = > {
import(/* webpackChunkName: "bar-chunk" */ "./bar.js");
import(/* webpackChunkName: "foo-chunk" */ "./foo.js");
}, 2000);
console.log(jquery, "main");
// src/bar.js
import react from "react";
console.log(react, "bar");
// src/foo.js
import vue from "vue";
import react from "react";
console.log(vue, react, "foo");
// webpack.config.js
module.exports = {
entry: './src/main.js'.output: {
filename: "[name].js",},optimization: { splitChunks: { chunks: "all"}},mode: "none",}Copy the code
After running the package, the third-party module jquery in main.js is extracted to vendor-main. js, the rest code is retained to main.js, and the third-party module Vue in foo.js is extracted to vendor-chunk. js. The rest of the code is stored in foo-chunk.js, the third-party modules in foo.js and bar.js are extracted to vendor-chunk-bar-chunk. js, and the rest of the code in bar.js is stored in bar-chunk.js.
Change the chunks property to Initial and package again. Only the third-party module jquery in main.js is extracted to vvendor ~main.js, and the rest code is reserved to main.js, while the third-party modules in foo.js and bar.js are not extracted. Just save it to foo-chunk.js and bar-chunk.js.
The chunks property is changed to async and then packaged again. The code in main.js is saved to main.js, but the third-party module is not extracted. The third-party module vue in foo.js is extracted to Vendor ~foo-chunk.js, and the remaining code is saved to foo-chunk.js. The react modules in foo.js and bar.js are extracted to vendor-chunk ~foo-chunk.js, and the remaining codes in bar.js are reserved to bar-chunk.js.
1.3.2 Matching conditions
minSize
: Minimum size of extracted code block before compression. Default is30000
(30KB
)maxSize
: The maximum size of the extracted code block before compression. Default is0
, that is, the size is not limitedminChunks
: Number of times the module is referenced. The default value is1
maxAsyncRequests
: Maximum load times on demand. The default is5
maxInitialRequests
: Maximum number of initial initial loading times. The default value is3
automaticNameDelimiter
: The extracted code block automatically generates a separator for the name. The default is ~name
: Specifies the name of the extracted code block filetrue
, that is, the file name is automatically generated
1.3.3 cache group
The cacheGroups default includes vendors and default rules, vendors being used to extract eligible modules from node_modules, and default acting on modules that are referenced multiple times. To disable a rule, set it to false.
Each item in a cacheGroups inherits or overrules the values of the parameters in the outer splitChunks, so, for example, if there is no minChunks attribute in the cacheGroups. Vendors item, it inherits the values of the outer splitchunk. minChunks attribute, Namely cacheGroups. Vendors. MinChunks to 1. If there is a minChunks attribute in the cacheGroups.default, it overwrites the attribute of the outer splitchunks. minChunks.
In addition to the above parameter values, cacheGroups provides three additional configuration properties.
test
: can match the module path orchunk
Name, default to all modulespriority
: indicates the extraction weight. A larger value indicates a higher priority. A module may satisfy multiple requirementscacheGroups
, which is extracted is controlled by the one with the highest weightreuseExistingChunk
: Whether to use the existing onechunk
.true
Represents if the currentchunk
The contained module has already been extracted, and no new modules will be generated
The root directory contains the webpack.config.js and SRC folders, while SRC contains main.js and utils.js.
// src/main.js
import Vue from "vue";
import Vuex from "vuex";
import VueRouter from "vue-router";
import { log } from "./utils.js";
console.log(Vue, Vuex, VueRouter, log, "main");
// src/utils.js
export function log() {
console.log("log");
}
// webpack.config.js
module.exports = {
entry: "./src/main.js".output: {
filename: "[name].js",},optimization: {
splitChunks: {
chunks: "all",}},mode: "none"};Copy the code
After running the package, the third party modules Vue, vuex, and VUE-Router are extracted to vendors~main.js, and the remaining main.js code and utils.js code are retained in main.js.
If you want to extract utils.js separately, you can customize the following cacheGroups, where module.resource is the full path containing the module name.
// webpack.config.js
splitChunks: {
chunks: "all".cacheGroups: {
utils: {
test: (module) = > {
return /src\\utils/.test(module.resource);
},
priority: -20.minSize: 0,},default: false,}}Copy the code
After packaging, third-party modules Vue, vuex, vuE-Router are extracted to vendors~main.js, codes from utils.js are extracted to vendors~main.js, and remaining codes from main.js are retained.
2 Production Environment
Production environments are different from development environments. Production environments focus on the user experience, how to make users load resources faster, including how to compress resources, adding environment variables to optimize packaging, and how to maximize browser caching.
2.1 Environment Configuration
2.1.1 Single configuration
Single configuration means using webpack.config.js regardless of the environment, simply passing in the environment variable parameters at the beginning of the build, and then using conditions in webpack.config.js to decide which configuration to use.
Note that Windows does not support ENV=development. The command will block and cause an error. The third-party plug-in cross-env can solve this problem.
cnpm i cross-env --save-dev
Copy the code
The root directory contains webpack.config.js and SRC folders, where SRC contains index.html and main.js.
// package.json{..."scripts": {
"dev": "cross-env ENV=development webpack-dev-server"."build": "cross-env ENV=production webpack"
},
"devDependencies": {
"webpack": "4.29.4"."webpack-cli": "3.2.3"."webpack-dev-server": "^ 3.1.14"."html-webpack-plugin": "^ 3.2.0"}}// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ENV = process.env.ENV;
const isProd = ENV === 'production';
module.exports = {
entry: './src/main.js'.output: {
filename: isProd ? "./[name].[chunkhash:8].js" : "./[name].js",},plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',}})]// src/index.html
<html lang="zh-CN">
<head>
<title>hello world</title>
</head>
<body>
<p>hello world</p>
</body>
</html>
// src/main.js
console.log("hello world");
Copy the code
Run the dev script command, and the console can view the packaged output file of the development environment.
Run the build script command to view the packaged output file.
2.1.2 Configuring multiple Environments
You can create a separate configuration file for each environment, for example, the development environment is webpack.dev.config.js, and the production environment is webpack.prod.config.js. However, the two configuration files usually have duplicate parts, which is not conducive to maintenance.
You can also create a separate webpack.config.js file, and then two other JS files reference that file and add their own environment configuration. However, the third-party plug-in Webpack-merge is generally used to merge webPack configurations, facilitating configuration management in different environments.
The root directories are build, package.json, and SRC folders. SRC contains index.html and main.js.
// package.json{..."scripts": {
"dev": "webpack-dev-server --config=./build/webpack.dev.config.js"."build": "webpack --config=./build/webpack.prod.config.js"}}Copy the code
The build includes webpack.config.js, webpack.dev.config.js, and webpack.prod.config.js.
// build/webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/main.js".plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",]}})// build/webpack.dev.config.js
const webpackConfig = require("./webpack.config.js");
module.exports = { ... webpackConfig,output: {
filename: "./[name].js",}}// build/webpack.prod.config.js
const webpackConfig = require("./webpack.config.js");
module.exports = { ... webpackConfig,output: {
filename: "./[name].[chunkhash:8].js",}}Copy the code
Run the dev and build scripts respectively, and the output is the same as the single configuration.
2.2 production mode
Earlier versions of WebPack had too many configuration items for different environments to use out of the box. Webpack4 has a new mode configuration item that allows you to switch the packaging mode.
If the following configuration items are in production, WebPack automatically adds the configuration items suitable for production.
// webpack.config.js
module.exports = {
...
mode: "production"
}
Copy the code
2.3 Environment Variables
DefinePlugin can be used in Webpack to add different environment variables for production and development environments, i.e. webpack.DefinePlugin is used to set global variables in the browser environment (which are not mounted to the window).
Webpack.defineplugin applies to all modules and completely replaces the environment variables in the module with the values set.
The root directories are webpack.config.js, package.json, and SRC, and SRC includes main.js.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"webpack": "4.29.4"."webpack-cli": "3.2.3"}}// webpack.config.js
const webpack = require('webpack')
module.exports = {
entry: './src/main.js'.output: {
filename: "./[name].js",},plugins: [
new webpack.DefinePlugin({
ENV: JSON.stringify('production'),})],mode: 'development'.devtool: 'none',}// src/main.js
console.log(ENV)
Copy the code
After packaging, look at the main.js file in the output directory dist, where ENV is completely replaced with “production”.
// dist/main.js
"./src/main.js": (function(module.exports) {
console.log("production");
})
Copy the code
Note that json. stringify is added to strings or objects containing strings. If json. stringify is not added, it will be the variable name instead of the string value after substitution, i.e. the ENV described above will be replaced with production (no string quotes).
In addition to string values, other types of environment variables can be set.
new webpack.DefinePlugin({
ENV: JSON.stringify('production'),
ENVIR: "\"development\"".IS_PRODUCTION: true.ENV_ID: 1038.CONSTANTS: JSON.stringify({
TYPES: ['foo'.'bar']})})Copy the code
Many frameworks and libraries use process.env.node_env as a variable to distinguish the development environment from the production environment. The value of production indicates that the current environment is the production environment. Libraries and frameworks will package without the development environment code such as warnings and logs. This will help reduce the size of the code and speed it up.
The process.env.node_env variable can be configured as follows. Note that if mode: ‘production’ is enabled, webapck automatically sets the value of process.env.node_env.
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify('development')})Copy the code
2.4 Differentiating environment modes
Against 2.4.1 scripts
Webpackage 4 introduces the mode attribute, including development, Production, and None modes.
The development mode sets the value of process.env.node_env in the module to development and enables the webpack plug-in for the development environment. Production mode sets the value of process.env.node_env in the module to production and enables the webpack plug-in for the production environment.
You can set mode in webpack.config.js or –mode in the scripts script command in package.json.
// package.json
"scripts": {
"build-dev": "webpack --mode=development"."build-prod": "webpack --mode=production"
}
Copy the code
Special scripts also default to mode, such as “dev”: “webpack-dev-server” mode is development, “build”: “webpack” mode is production.
// package.json
"scripts": {
"dev": "webpack-dev-server"."build": "webpack"
}
Copy the code
NODE_ENV (process.env.node_env, process.env.node_env, process.env.node_env, process.env.node_env, process.env.node_env, process.env.node_env) However, the current environment variable cannot be retrieved from the Node environment (webPack configuration file).
// package.json
"scripts": {
"build": "webpack"
}
// src/mian.js
console.log(process.env.NODE_ENV); // production
// webpack.config.js
console.log(process.env.NODE_ENV); // undefined
module.exports = {
entry: "./src/main.js". }Copy the code
In webpack.config.js, we can get environment variables as functions, but we can’t get environment variables outside the function either. The production in main.js is due to the special script “build”: “Webpack” defaults to Production mode.
// package.json
"scripts": {
"build-dev": "webpack --env=development"."build-prod": "webpack --env=production"
}
// webpack.config.js
console.log(process.env.NODE_ENV); // undefined
module.exports = (env) = > {
console.log(env); // development | production
return {
entry: "./src/main.js". }}// src/main.js
console.log(process.env.NODE_ENV); // production
Copy the code
Webpack 2.4.2. DefinePlugin
Scripts mode module process.env.node_env can only have a fixed number of values.
Webpack.defineplugin can set process.env.node_env to any value in the module, but can’t get the current environment variable in the Node environment.
// package.json
"scripts": {
"build": "webpack"
}
// webpack.config.js
const webpack = require("webpack")
console.log(process.env.NODE_ENV); // undefined
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js",},plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('dev'),}})]// src/main.js
console.log(process.env.NODE_ENV); // dev
Copy the code
2.4.3 cross – env
In the above two methods, environment variables cannot be obtained from the Node environment (Webpak configuration file), scripts can only be obtained from the function, and the current environment variables cannot be obtained from webpack.config.js.
Cross-env, a third-party plug-in, can fetch the current environment variable from the Node environment and set the value of the environment variable arbitrarily.
// package.json
"scripts": {
"build": "cross-env ENVIR=prod webpack"
},
"devDependencies": {
"cross-env": "^ 7.0.3." ". }// webpack.config.js
console.log(process.env.ENVIR); // prod
module.exports = {
entry: "./src/main.js". }// src/main.js
console.log(process.env.ENVIR); // undefined
Copy the code
2.5 the source map
Source map refers to the process of mapping compiled, compressed, and packaged code back to source code. Webpack packed and compressed code is almost unreadable, and the code throws errors that make it difficult to trace back to the call stack.
2.5.1 Configuration Not Enabled
The root directory contains package.json, webpack.config.js and SRC folders, while SRC contains index.html, main.js and style. SCSS. The main function of MiniCssExtractPlugin is to extract CSS style files.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"css-loader": "^ 0.28.9"."html-webpack-plugin": "^ 3.2.0"."node-sass": "^ 4.7.2." "."sass-loader": "^ 6.0.7"."mini-css-extract-plugin": "^ 0.5.0"."style-loader": "^ 0.19.0"."webpack": "4.29.4"."webpack-cli": "^ 3.2.3"}}// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: "./src/main.js".plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",}).new MiniCssExtractPlugin({
filename: "[name].css".chunkFilename: "[id].css",})],module: {
rules: [{test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader'.'sass-loader'],}]}}// src/index.html
<html lang="zh-CN">
<body>
<p>hello world</p>
</body>
</html>
// src/main.js
import './index.scss'
console.log('source-map')
// src/index.scss
$color: red;
p {
color: $color;
}
Copy the code
After executing the build script package, output index.html, main. CSS, main.js in the root directory, use the VS Code editor plug-in Live Server, inside index. See index.html deployed to the server.
View the output on the following console.
Formatted :formatted:76 Click mian.js:formatted:76 to jump to the following location, but you can only view the position of the output statement in the packaged code, not the source code.
Then look at the styles on the console.
Click mian.css:1 to jump to the following location. You can only view the packaged style code, but cannot go back.
2.5.2 Enabling the Configuration
In webpack.config.js add devTool to open js file source map, and for SCSS, CSS need to add additional Source map configuration items.
// webpack.config.js
module.exports = {
...
module: {
rules: [{test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader'.options: {
sourceMap: true}}, {loader: 'sass-loader'.options: {
sourceMap: true}}]}]},devtool: 'source-map'
}
Copy the code
Since the devtool configuration item is enabled, the source map will be passed along with the source code step by step until the final map file is generated. By default, the file name is output file with the. Map suffix, such as main.js.map. By default, a comment is appended to the end of main.js to identify the location of the map file.
// main.js.//# sourceMappingURL=main.js.map
Copy the code
The console looks at the output again and can already see the exact number of lines of this statement in the source code.
Click main.js:2 to jump to the following to see the details of this statement.
Styles can be traced back to the SCSS file.
Click index.scss:3 to jump to the following.
2.5.3 Security performance
After source Map is enabled, the parsed project source code can be found in the Developer tools webpack:// directory.
Note that when you open the browser’s developer tools, the map file is loaded at the same time, and the browser then uses it to parse the corresponding output file to figure out the directory structure and content of the source code.
Map files are usually large and cannot be loaded without opening developer tools, but using Source Map can be a security risk.
Webpack provides hidden-source-map and Nosource-source-map strategies to improve the security of source map.
Hidden-source-map still generates the full map file, but no reference to the map file is added to the output file. To trace the source, upload the map file using a third-party service, such as Sentry.
The nosource-source-map directory structure of the source code can be viewed in the developer tools, but the details of the file are hidden. You can see the exact number of lines in the console log, which is basically enough for backtracking errors.
2.6 DevTool Configuration Items
Devtool is used for debugging, including the following dozen configurations.
none
eval
eval-source-map
cheap-eval-source-map
cheap-module-eval-source-map
source-map
cheap-source-map
cheap-module-source-map
inline-source-map
inline-cheap-source-map
inline-cheap-module-source-map
hidden-source-map
nosources-source-map
These keywords include eval, cheap, module, source-map, etc. Most of them are combined, and their specific features are as follows.
eval
Use:eval
Package module code, and existssourceURL
.sourceURL
Point to original filecheap
Pack:map
File, does not save the column position information of the original code, only contains the row position information. ignoreloader
thesource map
And only the translated code is displayedmodule
Including:loader
thesourcemap
source-map
Generated by:.map
file
2.6.1 none
The root directory contains package.json, webpack.config.js, and SRC folders, while SRC contains index.html and main.js.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"@babel/core": "^ 7.2.2." "."@babel/preset-env": "^ 7.3.1"."babel-loader": "^ 8.0.5"."html-webpack-plugin": "^ 3.2.0"."webpack": "4.29.4"."webpack-cli": "^ 3.2.3"}}// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/main.js".plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",})],module: {
rules: [{test: /\.js$/,
loader: "babel-loader".exclude: /node_modules/,
options: {
cacheDirectory: true.presets: [["@babel/preset-env", { modules: false}]],},},],},devtool: "none".mode: "development"
}
// src/index.html
<html lang="zh-CN">
<body>
<p>hello world</p>
</body>
</html>
// src/main.js
const fn = () = >{
console.log('source map');
}
fn();
Copy the code
After running the build script command, the console views the output.
Click mian.js:97 to jump to the following.
The None configuration item cannot be traced back to the source code and simply translates the module code based on the configured Loader.
2.6.2 eval
Change the devTool property in webpack.config.js to eval, and run the build script command to see the packaged output.
The console view the output and click to jump to.
In the EVAL configuration item, the packaged module code is wrapped in EVAL, including the sourceURL, and the traceable source code is converted by the Loader (the arrow function is converted to a normal function) with cursor column information.
2.6.3 source – the map
Change the devtool property in webpack.config.js to source-map, run the build script again, and more. Map files are generated in dist directory.
View the code for the packaged output.
The console view the output and click to jump to.
SourceMappingURL is appended to the end of the packaged output code, and the backtracked source code is the original code with cursor column information.
2.6.4 being – the source – the map
Change the devtool property in webpack.config.js to cheap-source-map, run the build script command, also generate. Map file in dist directory, package the output code is the same as source-map.
The console view the output and click to jump to.
The sourceMappingURL is appended to the end of the packaged output code. The traceback source code is converted by the Loader (the arrow function is converted to a normal function) without cursor column information (the cursor is at the beginning of the line).
2.6.5 eval – source – the map
Change the devtool property in webpack.config.js to eval-source-map and run the build script command to view the packaged output.
The console view the output and click to jump to.
In the eval-source-map configuration item, the packaged module code is wrapped in eval, including sourceURL and sourceMappingURL, without generating a. Map file. Instead, convert the map file contents to Base64 encoding and insert them after the sourceMappingURL, tracing back to the original source code with cursor column information.
2.6.6 being – eval – source – the map
Change the devtool property in webpack.config.js to cheap-eval-source-map and run the build script command to view the packaged output.
The console view the output and click to jump to.
In the cheap-eval-source-map configuration item, the packaged module code is wrapped in eval, including sourceURL and sourceMappingURL, without generating a. Map file. Instead, the map file content is converted to Base64 encoding and inserted after the sourceMappingURL. The backtracked source code is converted by loader (arrow function to normal function) without cursor column information (cursor at the beginning of line).
2.6.7 being – the module – the eval – source – the map
Change the devtool property in webpack.config.js to cheap-module-eval-source-map and run the build script command to view the packaged output.
The console view the output and click to jump to.
In the cheap-module-eval-source-map configuration item, the module code is wrapped in eval, including sourceURL and sourceMappingURL, without generating a. Map file. Instead, convert the map file content to Base64 encoding and insert it after the sourceMappingURL, tracing back to the original source code without cursor column information (the cursor is at the beginning of the line).
2.6.8 being – the module – the source – the map
Change the devtool property in webpack.config.js to cheap-module-source-map and run the build script command to view the packaged output.
The console view the output and click to jump to.
The.map file is also generated in the cheap-module-source-map configuration item, and the sourceMappingURL is appended to the end of the packaged output code. The backtraced source code is the original code without cursor column information (the cursor is at the beginning of the line).
2.6.9 inline – source – the map
Change the devtool property in webpack.config.js to inline-source-map, run the build script command, and see the package output.
The console view the output and click to jump to.
In the inline-source-map configuration item, a. Map file is not generated, but the map file content is converted to base64 encoding and inserted after the sourceMappingURL, with the source code backtracked to the original code with cursor column information.
2.6.10 hidden – source – the map
Change the devtool property in webpack.config.js to hidden-source-map, run the build script command, which will also generate a. Map file in the dist directory, and view the packaged output code.
The console view the output and click to jump to.
In the inline-source-map configuration item, a. Map file is generated, but no reference to the map file is kept (no sourceMappingURL) and no source code can be traced back.
2.6.11 nosources – source – the map
Change the devtool attribute in webpack.config.js to nosource-source-map, run the build script command, and view the output on the console.
Click main.js:2 to jump to the following.
View the code for the packaged output.
The sourceMappingURL is appended to the end of the packaged output code, which cannot be traced back to the source code (the directory structure of the source code can be viewed, the specific content is hidden). However, you can view the exact number of lines in the console log.
2.6.12 Comparison of differences
Slowest > fast > OK > slow > slowest > slowest are the differences among configuration items shown below.
devtool | Build speed | The map way | The eval package | sourceMappingURL | Whether there is cursor column information | Traceable or not | Back in the code |
---|---|---|---|---|---|---|---|
none |
fastest |
– | – | – | – | no | – |
eval |
fast |
eval Within the functionsourceURL Reference the source file path |
is | – | There are | is | loader Translated code |
source-map |
slowest |
Module tail appendsourceMappingURL referencemap |
no | linkmap The file name |
There are | is | The original code |
cheap-source-map |
ok |
Module tail appendsourceMappingURL referencemap |
no | linkmap The file name |
no | is | loader Translated code |
eval-source-map |
slowest |
eval Within the functionsourceURL Reference the source file path and insert the function at the endsourceMappingURL |
is | map contentbase64 coding |
is | is | The original code |
cheap-eval-source-map |
ok |
eval Within the functionsourceURL Reference the source file path and insert the function at the endsourceMappingURL |
is | map contentbase64 coding |
no | is | loader Translated code |
cheap-module-eval-source-map |
slow |
eval The sourceURL in the function refers to the source file path, and the end of the function is insertedsourceMappingURL |
is | map contentbase64 coding |
no | is | The original code |
cheap-module-source-map |
slow |
Module tail appendsourceMappingURL referencemap |
no | linkmap The file name |
no | is | The original code |
inline-source-map |
slowest |
Module tail appendsourceMappingURL |
no | map contentbase64 coding |
is | is | The original code |
hidden-source-map |
slowest |
– | no | – | – | – | – |
nosources-source-map |
slowest |
Module tail appendsourceMappingURL referencemap |
no | linkmap The file name |
– | – | – |
2.7 Resource Compression
Generally, resources published to the online environment are compressed (uglified), that is, redundant whitespace, line breaks and unexecutable code are removed, variable names are shortened, comments are removed, and code is replaced with a shorter form without changing the execution result.
The overall volume of the code will be significantly reduced after compression, and it will be basically unreadable, which improves the security of the code to a certain extent.
2.7.1 compressed JavaScript
Webpack3 and below by webpack. Optimize. UglifyJsPlugin implementation code compression.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"webpack": "^ 3.10.0"}}// webpack.config.js
const webpack = require("webpack")
module.exports = {
entry: "./src/main.js".output: {
filename: "./dist/[name].js"
},
plugins: [new webpack.optimize.UglifyJsPlugin()]
}
// src/main.js
const fn = function () {
console.log("hello world")
}
fn()
Copy the code
Webpack4 uses terser-webpack-plugin as the built-in compression plug-in by default, which supports compression of ES6+ code. In Webpack4, optimization. Minimize can be used to control whether compression is enabled, which is disabled by default in the development environment and enabled by default in the production environment.
// webpack.config.js
module.exports = {
...
optimization: {
minimize: true}}Copy the code
You can also customize the optimization. Minimizer compression plug-in and its configuration items, which automatically remove console.log when packaged as follows.
// webpack.config.js
const TerserPlugin = require("terser-webpack-plugin")
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js"
},
optimization: {
minimize: true.minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
pure_funcs: ["console.log"]}})]}}Copy the code
2.7.2 compress CSS
The CSS is first extracted using extract-text-webpack-plugin or mini-CSS-extract-plugin, and then compressed using optimize- CSS-assets-webpack-plugin.
The root directories are package.json, webpack.config.js and SRC folders. SRC contains main.js and index.css.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"css-loader": "^ 0.28.7"."mini-css-extract-plugin": "^ 0.5.0"."optimize-css-assets-webpack-plugin": "^ 4.0.1." "."webpack": "4.29.4"."webpack-cli": "^ 3.2.3"}}// webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js",},module: {
rules: [{test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
"css-loader"]]}},plugins: [
new MiniCssExtractPlugin({
filename: "[name].css".chunkFilename: "[id].css"}),].optimization: {
minimize: true.minimizer: [new OptimizeCSSAssetsPlugin()]
},
}
// src/main.js
import "./index.css";
console.log("hello world");
// src/index.css
p {
color: red;
}
Copy the code
After the build script is packaged, view the output CSS file.
// dist/main.css
p{color:red}
Copy the code
2.8 the cache
Caching is the reuse of resources that the browser has acquired. Detailed caching policies (such as cache duration) are determined by the server, and the browser responds with the local cache until the resource expires.
However, there is a problem with this approach. If your code is bug-fixed and you want to update it to all users’ browsers at once, your best bet is to change the resource URL, forcing the client to re-download the resource.
The common approach is to hash the resource content once per package and store it as the version number in the file name.
2.8.1 version number
The following uses chunkhash as the file version number, and separately calculates a hash value for each chunk.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"webpack": "4.29.4"."webpack-cli": "^ 3.2.3"}}// webpack.config.js
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name]@[chunkhash:8].js"}}// src/main.js
console.log("hello world");
Copy the code
Run the build script package as follows.
The change of resource file name means the change of reference path in HTML. The html-webpack-plugin can be used to automatically synchronize the reference resource name after wrapping.
// package.json
"devDependencies": {..."html-webpack-plugin": "3.2.0"
}
// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"}})]// src/html
<html lang="zh-CN">
<body>
<p>hello world</p>
</body>
</html>
Copy the code
Run the build script package again and open index.html under dist where the referenced resource paths are synchronized.
2.8.2 CommonsChunkPlugin
The CommonsChunkPlugin allows you to separate out infrequently changing code from frequently iterating business code, and these resources can be cached on the client side.
However, the IDS specified for each module in Webpack3 and the following are numerically increasing. When a new module is inserted, the IDS of other modules will change, thus affecting the contents of chunk, that is, the value of chunkhash, causing the resources that need not be downloaded to be downloaded again.
The root directories are package.json, webpack.config.js and SRC folders, and SRC contains main.js.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"webpack": "^ 3.10.0"
},
"dependencies": {
"jquery": "^ 3.2.1." "}}// webpack.config.js
const webpack = require("webpack")
module.exports = {
entry: {
main: "./src/main.js".vendor: ["jquery"]},output: {
filename: "./dist/[name]@[chunkhash:8].js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor".filename: "./dist/[name]@[chunkhash:8].js"}})]// src/main.js
import "jquery";
console.log("main.js");
Copy the code
Run the build script package to see the output resources, where vendor contains only third-party modules jquery and WebPack runtime files.
SRC > utils. Js, main.js.
// src/main.js
import "jquery";
import "./utils";
console.log("main.js");
// src/utils.js
console.log("utils.js");
Copy the code
Execute the build script package again and view the output resources.
This creates a problem where the module in Vendor does not change, but its path name does.
By comparing [email protected] and [email protected] in dist directory, only the following two places have changed.
2.8.3 HashedModuleIdsPlugin
The solution is to change the way module IDS are generated. The HashedModuleIdsPlugin plugin comes with WebPack3, which can generate a string hash ID for each module based on its path.
// src/main.js
import "jquery";
console.log("main.js");
// webpack.config.js
const webpack = require("webpack")
module.exports = {
entry: {
main: "./src/main.js".vendor: ["jquery"]},output: {
filename: "./dist/[name]@[chunkhash:8].js"
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: "vendor".filename: "./dist/[name]@[chunkhash:8].js"}})]Copy the code
Run the build script package to view the output resources.
Main.js introduces utils.js.
import "jquery";
import "./utils";
console.log("main.js");
Copy the code
Run the Build package again.
Since vendor only includes the third-party module jquery and webPack runtime, and the path of jquery is always fixed, its hash ID is always fixed.
Versions below Webpack3, which do not support string ids, can use the webpack-hashed-module-id-plugin. Webpackage 4+ has modified the way the module ID is generated, so this problem is no longer present.
2.9 Monitoring Analysis
You can use third-party plug-ins to monitor and analyze the volume of the bundle output to prevent unnecessary redundant modules from being added.
2.9.1 Import Cost
The Import Cost in Vs Code checks the size of imported modules in real time. When a new module is referenced in Code (mainly in node_modules), the compressed and gzip size of the module is calculated.
2.9.2 webpack – bundle – analyzer
Another visual analysis tool is Webpack-bundle-Analyzer, which analyzes the composition of a bundle.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"vue": "^ 2.6.12." "."vue-router": "^ 3.5.1 track of"."vuex": "^ 3.6.2." "."webpack": "^ 3.10.0"."webpack-bundle-analyzer": "^ 4.4.1." "}}// webpack.config.js
const webpack = require("webpack");
const Analyzer = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = {
entry: {
main: "./src/main.js".vendor: ["vue"."vuex"."vue-router"]},output: {
filename: "./dist/[name].js"
},
plugins: [
new Analyzer(),
new webpack.optimize.CommonsChunkPlugin({
name: "vendor".filename: "./dist/[name].js"}})]// src/main.js
import Vue from "vue";
import Vuex from "vuex";
import VueRouter from "vue-router";
import { log } from "./utils.js";
console.log(Vue, Vuex, VueRouter, log, "main.js");
// src/utils.js
export function log() {
console.log("utils.js");
}
Copy the code
Run the build script and the result is as follows.
3 Packaging Optimization
Do not take any optimization points into the project at the beginning of the project, which will increase the complexity and the optimization effect is not ideal. Generally, the project develops to a certain scale, and then specific optimization occurs when performance problems occur.
3.1 HappyPack
HappyPack is a plugin that uses multiple threads to speed up webpack packaging.
The time-consuming part of the packaging process is to translate various resources through loader, such as Babel ES6+, etc. The specific workflow is as follows.
- Get the package entry from the configuration
- matching
loader
Rules, and translate the entry module - Dependency lookup is performed on translated modules
- Repeat the steps for the newly found module
2
And the steps3
Until there are no new dependency modules
Steps 2 through 4 are a recursive process in which WebPack takes the deeper resources step by step and then translates them one by one. The fundamental problem is that WebPack is single-threaded, and if a module depends on several other modules, WebPack must translate them one by one. Although these translation tasks are not task dependent on each other, they must be executed serially. HappyPack’s core feature is the ability to start multiple threads to translate different modules in parallel, making full use of local resources to speed up packaging.
3.1.1 single loader
To use the HappyPack plug-in, replace the original loader with the loader provided by HappyPack and send the original Loader to the HappyPack plug-in.
The root directories are package.json, webpack.config.js, and SRC, where SRC includes main.js, bar.js, and foo.js.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"@babel/core": "^ 7.2.2." "."@babel/preset-env": "^ 7.3.1"."babel-loader": "^ 8.0.5"."vue": "^ 2.6.12." "."vuex": "^ 3.6.2." "."webpack": "4.29.4"."webpack-cli": "3.2.3"."happypack": "^ 5.0.0"}}// src/main.js
import "./foo.js";
import "./bar.js";
console.log("main.js");
// src/foo.js
import vue from "vue";
console.log(vue, "foo.js");
// src/bar.js
import Vuex from "vuex";
console.log(Vuex, "bar.js");
Copy the code
The initial WebPack configuration is as follows.
// webpack.config.js
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js",},module: {
rules: [{test: /\.js$/,
loader: "babel-loader".exclude: /node_modules/,
options: {
cacheDirectory: true.presets: [["@babel/preset-env", { modules: false}}]}}Copy the code
Change the WebPack configuration after introducing HappyPack.
// webpack.config.js
const HappyPack = require("happypack");
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js"
},
module: {
rules: [{test: /\.js$/,
exclude: /node_modules/,
loader: "happypack/loader"}},plugins: [
new HappyPack({
loaders: [{loader: "babel-loader".options: {
cacheDirectory: true.presets: [["@babel/preset-env", { modules: false}]}}]})]}Copy the code
3.1.2 multiple loader
When HappyPack optimizes multiple Loaders, it needs to configure an ID for each loader. Otherwise, HappyPack cannot know how rules and plugins correspond one by one.
The root directories are package.json, webpack.config.js, and SRC. SRC contains main.js and index.css.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"@babel/core": "^ 7.2.2." "."@babel/preset-env": "^ 7.3.1"."babel-loader": "^ 8.0.5"."webpack": "4.29.4"."webpack-cli": "3.2.3"."happypack": "^ 5.0.0"."css-loader": "^ 0.28.9"."style-loader": "^ 0.19.0"}}// src/main.js
import "./index.css";
console.log("main.js");
// src/index.css
p {
color: red;
}
Copy the code
The initial WebPack configuration is as follows.
// webpack.config.js
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js",},module: {
rules: [{test: /\.js$/,
loader: "babel-loader".exclude: /node_modules/,
options: {
cacheDirectory: true.presets: [["@babel/preset-env", { modules: false}]]}}, {test: /\.css$/,
use: ["style-loader"."css-loader"}]}}Copy the code
Change the WebPack configuration after introducing HappyPack.
// webpack.config.js
const HappyPack = require("happypack");
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js"
},
module: {
rules: [{test: /\.js$/,
loader: "happypack/loader? id=js".exclude: /node_modules/
},
{
test: /\.css$/,
loader: "happypack/loader? id=css"}},plugins: [
new HappyPack({
id: "js".loaders: [{loader: "babel-loader".options: {
cacheDirectory: true.presets: [["@babel/preset-env", { modules: false}]]}}]}),new HappyPack({
id: "css".loaders: ["style-loader"."css-loader"]]}})Copy the code
3.2 Narrowing the packaging scope
There are two ways to improve performance: increase resources or narrow the scope. Increasing resources means using more CPU and memory and more computing power to shorten the execution time of tasks. Narrow it down to the task itself, such as eliminating redundant processes or not doing repetitive work.
3.2.1 exclude/include
For JS modules, the node_modules directory is usually excluded.
3.2.2 noParse
Some third-party libraries do not want WebPack to parse at all, that is, they do not want to apply any Loader rules and have no internal dependencies on other modules, so they can be ignored using noParse.
Ignore all modules whose file names include Lodash. These modules will still be packaged into the resource, but WebPack will not parse them.
// webpack.config.js
module.exports = {
...
module: {
noParse: /lodash/
}
Copy the code
3.2.3 IgnorePlugin
The IgnorePlugin plug-in is able to exclude modules completely, and excluded modules are not packaged into resource files even if they are referenced.
Moment.js is a time-processing library. It loads a lot of language packages for localization, which is generally not useful and takes up a lot of volume.
The root directory contains package.json, webpack.config.js, and SRC, and the SRC directory contains main.js.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"moment": "^ 2.29.1"."webpack": "4.29.4"."webpack-cli": "3.2.3"}}// webpack.config.js
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js",}}// src/main.js
import "moment";
console.log("main.js");
Copy the code
Run the build script package to view the output resources.
Modify webpack.config.js, where resourceRegExp matches resource files and contextRegExp matches search directories.
// webpack.config.js
const webpack = require("webpack");
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js",},plugins: [
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/}})]Copy the code
Run the build script package again to see the output resources.
3.3 DllPlugin
Early Windows system due to the limited computer memory space and smaller problems appeared a kind of memory optimization method called dynamic link library, when a same subroutine is called multiple programs, in order to reduce memory consumption, this joke program can be stored as an executable file, when called by multiple programs in memory only to generate and use the same instance.
The DllPlugin builds on this idea by pre-compiling and packaging third-party modules, or modules that change infrequently, and then using them during the actual build process in a project. Pre-packaging also generates a list of modules, which will act as a link and index when engineering business modules are packaged.
Similar to Code Splitting, DllPlugin can be used to extract common modules, but there are also essential differences. Code Splitting is setting up specific rules and extracting modules based on those rules during the packaging process. DllPlugin completely separates vendor, has its own set of Webpack configuration and packages it independently, no longer handles it in the actual project construction, and can be directly used. Theoretically, DllPlugin can be faster in packaging than Code Splitting, but it also increases the complexity of configuration and resource management. It can be understood that DllPlugin replaces one packaging with two packaging, which is theoretically faster, the first packaging is for modules that don’t change often, and the second packaging is for business modules.
3.3.1 DLL packaging
The root directory contains webpack.config.js, package.json, and SRC, while SRC contains main.js and index.html.
// package.json{..."scripts": {
"build": "webpack"
},
"devDependencies": {
"vue": "^ 2.6.12." "."webpack": "4.29.4"."webpack-cli": "3.2.3"}}// src/main.js
import "vue";
console.log("main.js");
Copy the code
Start by creating a separate WebPack configuration file for the dynamic link library and name it webpack.dll.config.js. Filename is the name of the dynamic link library, output.path is the output path of the dynamic link library, and output.library must be the same as name in DllPlugin. In DllPlugin, path is the output path of the module list of the dynamic link library, and path is the value of the name field in the manifest.json file.
// webpack.dll.config.js
const path = require("path");
const webpack = require("webpack");
const dllAssetPath = path.join(__dirname, "dist"."dll");
const dllLibraryName = "dll";
module.exports = {
entry: ["vue"].output: {
path: dllAssetPath,
filename: "dll.js".library: dllLibraryName
},
plugins: [
new webpack.DllPlugin({
name: dllLibraryName,
path: path.join(dllAssetPath, "manifest.json")]}})Copy the code
Then configure package.json and add a script command.
// package.json{..."scripts": {
"dll": "webpack --config webpack.dll.config.js"}}Copy the code
Running the DLL script command will generate dlL. js and manifest.json in the DLL folder in the dist directory. The variable name in dlL. js is output.library in the above configuration, and the name in manifest.json is the name in the above DllPlugin.
// dist/dll/dll.js
var dll = (function (params) {... }) (...).// dist/dll/manifest.json
{
"name": "dll"."content": {... }}Copy the code
Finally, you need to link dlL.js in your business code.
// webpack.config.js
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: "./src/main.js".output: {
filename: "./[name].js"
},
plugins: [
new webpack.DllReferencePlugin({
manifest: require(path.join(__dirname, "dist/dll/manifest.json"))]}})Copy the code
The global variable DLL will be declared when the page is executed to dlL. js, and the manifest is equivalent to injecting the resource map of main.js. Main.js will first find the library named DLL through the name field. Therefore, output.library in webpack.dll.config.js must be the same as the name in DllPlugin.
// src/index.html
<html lang="zh-CN">
<body>
<p>hello world</p>
<script src="./dll/dll.js"></script>
<script src="./main.js"></script>
</body>
</html>
Copy the code
3.3.2 rainfall distribution on 10-12 id problem
Look at manifest.json, where each module has an ID that increases in numerical order, and the main.js code references a numerical ID when referring to a module in dlL.js.
DLL. Js (built by DllPlugin), utils.j@ [chunkhash].js, main.js may exist after the project is packaged. DLL. Js contains vue, whose ID is 5. When trying to add more modules to dlL.js, the Vue id changes to 6 after the rebuild.
Since Utils also references the vue module, its chunkhash will change after the rebuild, but the content itself will not change, and the client user will only have to re-download the resource.
The solution to this problem is to use the HashedModuleIdsPlugin plug-in.
// webpack.dll.config.js
module.exports = {
plugins: [
new webpack.DllPlugin({
...
}),
new webpack.HashedModuleIdsPlugin()
]
}
Copy the code
3.4 the tree shaking
ES6 Module dependencies are built at compile time, not run time. Based on this feature, WebPack provides Tree Shaking, which detects modules that are not referenced in the project during packaging. This part of the code will never be executed, hence the name “dead code”.
Webpack marks this code and removes it from the final bundle when the resource is compressed.
The following Webpack package adds a tag to the bar function and then uses the compression tool to remove dead code.
// src/main.js
import { foo } from "./utils.js";
foo();
// src/utils.js
export function foo() {
console.log("foo");
}
export function bar() {
console.log("bar");
}
Copy the code
In the previous
The next article