This paper mainly optimized two aspects: 1. Optimized development experience 2. Optimized output quality
Optimize the development experience
- Promote efficiency
- Optimize construction speed
- Optimize the user experience
Optimize output quality
- Optimize the code to be posted online to reduce load time perceived by users
- Improve code performance, good performance, fast execution
The following is optimized based on Webpack4.
Reduce the file scope Loader
- Optimizing loader Configuration
- Test, include, and exclude to narrow the processing scope of the Loader
- Recommendations include
include: path.resolve(__dirname, "./src"),
Copy the code
Build time before optimization:
Time: 2429ms
Modify code optimization:
rules: [
{
test: /\.css$/,
include: path.resolve(__dirname, "./src"),
use: ["style-loader"."css-loader"],}, {test: /\.less$/,
include: path.resolve(__dirname, "./src"),
use: [
// "style-loader",
MiniCssExtractPlugin.loader,
{
loader: "css-loader".options: {
/ / CSS modules
modules: true,}}, {loader: "postcss-loader",},"less-loader",]}, {test: /\.(png|jpe? g|gif)$/,
include: path.resolve(__dirname, "./src"),
use: {
loader: "url-loader".options: {
name: "[name]_[hash:6].[ext]".outputPath: "images/".// Url-loader is recommended because url-loader supports limit
// It is recommended to convert small image resources to Base64
limit: 12 * 1024.// the unit is byte 1024=1kb}},}, {test: /\.js$/,
include: path.resolve(__dirname, "./src"),
exclude: /node_modules/,
use: {
loader: "babel-loader",}},],Copy the code
Build time after optimization:
Time: 2110ms
To reduce the 319 ms
To optimize the resolve. Modules
Resolve. modules is used to configure the directory in which Webpack looks for third-party modules. The default is [‘node_modules’]. The default is to look for a third party in the node_modules directory of the current project. If no third party is found, it will go to the upper directory.. /node_modules find, no more will go to.. /.. /node_modules, and so on, is similar to node.js’s module finding mechanism.
If our third party modules are installed in the project root directory, we can specify this path directly.
module.exports={
resolve: {modules: [path.resolve(__dirname, "./node_modules")]}}Copy the code
Build time after optimization:
Time: 1532ms
To reduce the 578 ms
To optimize the resolve. Alias
Resolve. Alias configures the alias to map the original import path to a new one. Take react as an example. The React library has two sets of code:
-
cjs
Modular code using commonJS specification
-
umd
Complete code is packaged, not modularized, and can be executed directly
By default, Webpack recursively parses and processes dependent files from the entry file./node_modules/bin/react/index. We can specify the file directly to avoid this time consuming area.
resolve: {
// Look for third-party optimizations
modules: [path.resolve(__dirname, "./node_modules")].alias: {
"@": path.join(__dirname, "./src"),
react: path.resolve(__dirname, "./node_modules/react/umd/react.production.min.js"),
"react-dom": path.resolve(__dirname, "./node_modules/react-dom/umd/react-dom.production.min.js")}},Copy the code
Build time after optimization:
Time: 1362ms
To reduce the 170 ms
To optimize the resolve. Extensions
Resolve. Extensions import statements without file suffixes and WebPack will try to find out if the file exists.
Default value:
extensions:['.js'.'.json'.'.jsx'.'.ts']
Copy the code
- Try to keep the list of suffixes small
- Import statements should be suffixed as much as possible.
It is not recommended to use ExtensionX if you want to optimize to the extreme, as it can consume some performance. Although it can bring some convenience.
Externals was used to optimize CDN static resources
We can store some JS files on the CDN (reducing the size of the JS packed by Webpack) and import them in index.html with tags such as:
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root">root</div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</body>
</html>
Copy the code
We want to be able to still reference it as import (e.g. Import $from ‘jquery’), and we want webPack not to package it, so we can configure externals.
//webpack.config.js
module.exports = {
/ /...
externals: {
// When jquery is introduced via script, you have jquery variables globally
'jquery': 'jQuery'}}Copy the code
Using static resource path publicPath(CDN)
By deploying resources around the world, CDN enables users to access resources nearby and speeds up access. To access the CDN, you need to upload static resources of web pages to the CDN service. When accessing these resources, the URL provided by the CDN service is used.
##webpack.config.js
output: {publicPath: '//cdnURL.com'.// Specify the CDN address for storing JS files
}
Copy the code
Conditions of use:
- The company must have a CDN server address
- Ensure that static resource files are uploaded or not
Extract the CSS using the MiniCssExtractPlugin
npm install mini-css-extract-plugin -D
Copy the code
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
{
test: /\.less$/,
use: [
/ / "style - loader", / / no longer need to style - loader, use MiniCssExtractPlugin. Loader instead
MiniCssExtractPlugin.loader,
"css-loader"./ / compile CSS
"postcss-loader"."less-loader" / / compile less]},plugins: [
new MiniCssExtractPlugin({
filename: "css/[name]_[contenthash:6].css".chunkFilename: "[id].css"})]Copy the code
Compress CSS
- Help optimize – CSS – assets – webpack – the plugin
- With the help of cssnano
First installation:
npm install cssnano -D
npm i optimize-css-assets-webpack-plugin -D
Copy the code
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
new OptimizeCSSAssetsPlugin({
cssProcessor: require("cssnano"), // Introduce the CSsnano configuration compression option
cssProcessorOptions: {
discardComments: { removeAll: true}}})Copy the code
Compressed HTML
With the aid of HTML – webpack – the plugin
new htmlWebpackPlugin({
title: Jingdong Mall.template: "./index.html".filename: "index.html".minify: {
// Compress HTML files
removeComments: true.// Remove comments from HTML
collapseWhitespace: true.// Remove whitespace and newline characters
minifyCSS: true // Compress inline CSS}}),Copy the code
The Development vs Production model differentiates packaging
First installation
npm install webpack-merge -D
Copy the code
Create the webapck.dev.js file
const {merge} = require("webpack-merge")
const commonConfig = require("./webpack.common.js")
const devConfig = {
...
}
module.exports = merge(commonConfig,devConfig)
Copy the code
Create the webpack.prod.js file
const {merge} = require("webpack-merge")
const commonConfig = require("./webpack.common.js")
const prodConfig = {
...
}
module.exports = merge(commonConfig,prodConfig)
Copy the code
package.js
"scripts": {"dev":"webpack-dev-server --config ./webpack.dev.js"."build":"webpack --config ./webpack.prod.js"
}
Copy the code
Based on environment variables
With the help of a cross – env
npm i cross-env -D
Copy the code
Configure command scripts in package, passing in parameters
"test:build": "cross-env NODE_ENV=production webpack --config ./webpack.test.config.js"."test:dev": "webpack-dev-server --config ./webpack.test.config.js"
Copy the code
Get the parameters in webpack.config.test.js to judge
const baseConfig = require("./webpack.config.base.js");
const devConfig = require("./webpack.config.dev.js");
const prodConfig = require("./webpack.config.prod.js");
const {merge} = require("webpack-merge");
console.log(process.env.NODE_ENV)
module.exports = (process.env.NODE_ENV) = >{
if(process.env.NODE_ENV && process.env.NODE_ENV.production){
return merge(commonConfig,prodConfig)
}else{
return merge(commonConfig,devConfig)
} }
Copy the code
tree Shaking
Webpack2. x is starting to support the concept of tree shaking.
Dead Code generally has the following characteristics:
- Code will not be executed, not reachable
- The results of code execution are not used
- The code only affects dead variables (write, not read)
- Js Tree Shaking supports only the introduction of ES Modules !!!!
Css tree shaking
npm i glob-all purify-css purifycss-webpack --save-dev
Copy the code
const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')
plugins: [new PurifyCSS({
paths: glob.sync([
// To create a path file for CSS Tree Sharking
path.resolve(__dirname, "./src/*.html"),// Tree shaking is also needed for HTML files
path.resolve(__dirname, "./src/*.js")])}),]Copy the code
JS tree shaking
Only import is supported. Commonjs is not supported
Example: Add the expo.js file
//expo.js
export const add = (a, b) = > {
return a + b;
};
export const minus = (a, b) = > {
return a - b;
};
Copy the code
//index.js
import { add } from "./expo";
add(1.2);
Copy the code
//webpack.config.js
optimization: {
usedExports: true // Which exported modules are used, then package
}
Copy the code
Developer tree shaking mode production is valid as long as mode is production, developer tree shaking mode is invalid as webpack is designed to make it easy for you to debug.
You can view the packaged code comments to see if they work.
This function is enabled by default.
SideEffects deals with sideEffects
//package.json
"sideEffects":false // Tree shaking is normal for all modules, only in production mode, with usedExports
Copy the code
Or exclude modules from the array that don’t need tree shaking
"sideEffects": ['*.css'.'@babel/polyfill']
Copy the code
Code Splitting
Single page application SPA:
Once packaged, only one bundle.js is generated for all pages.
- The code gets bigger, making it harder to download
- Poor utilization of browser resources
Multi-page application MPA:
If multiple pages introduce some common modules, you can pull those common modules out and package them separately. Common code only needs to be downloaded once and then cached, avoiding repeated downloads.
If we introduce a third party library that is 1MB in size and our business logic code is 1MB in size, the package size will be 2MB, which will cause problems:
- The file size is large and the loading time is long.
- The business logic changes, but the third-party tool library does not. Therefore, when the business logic changes, the third-party tool library also changes.
For example, we use the third-party library LoDash:
import _ from "lodash";
console.log(_.join(['a'.'b'.'c'.'* * * *']))
Copy the code
In fact, the concept of code Splitting is not directly related to Webpack, but Webpack provides a more convenient way for us to realize code Splitting.
Based on the webpack.js.org/plugins/spl…
optimization: {
splitChunks: {
chunks: "all".// All common chunks of the chunks code are separated into a single file}},Copy the code
The dist directory now contains a file, vendors~main.js, that is loDash out
Other configurable items of splitChunks:
optimization: {
splitChunks: {
chunks: "async".// For synchronous initial, asynchronous async, all modules
minSize: 30000.// Minimum size, when the module is larger than 30KB
maxSize: 0.// This is not recommended
minChunks: 1.// At least a few chunks of the generated chunk file reference this module
maxAsyncRequests: 5.// Maximum number of asynchronous requests. Default: 5
maxInitialRequests: 3.// Maximum initialization request, entry file synchronization request, default 3
automaticNameDelimiter: "-".// Package the split symbol
name: true.// The packaged name can accept a function in addition to a Boolean value
cacheGroups: {
/ / cache group
vendors: {
test: /[\\/]node_modules[\\/]/,
name: "vendor".// The name of the separated chunk to cache
priority: -10.// Cache group priority A larger number indicates a higher priority
},
other: {
chunks: "initial"./ / must choose three: "initial" | | "all" "async" (the default is async)
test: /react|lodash/.// Regular rule validation, if match, extract chunk,
name: "other".minSize: 30000.minChunks: 1,},default: {
minChunks: 2.priority: -20.reuseExistingChunk: true.// You can set whether to reuse the chunk}},}},Copy the code
We can configure cache groups
splitChunks: {
chunks: "all".// All common chunks of the chunks code are separated into a single file
automaticNameDelimiter: The '-'.// Package the split symbol
cacheGroups: {
lodash: {
test:/lodash/,
name: "lodash"
},
react: {
test: /react|react-dom/,
name: "react"}}},Copy the code
After packaging
Generally, use the following configuration:
optimization:{
// help us do automatic code splitting
splitChunks: {chunks:"all".// Asynchrony is supported by default, we use all}}Copy the code
Scope Hoisting
Scope promotion refers to that Webpack analyzes the dependencies among modules through static analysis of ES6 syntax, and tries to put modules into the same function as much as possible. Here’s a code example to understand:
A new hello. Js
export default 'Hello, Webpack';
Copy the code
A new index. Js
import str from './hello.js';
console.log(str);
Copy the code
Once packaged, the contents of Hello.js and index.js are separated
We through configuration optimization. ConcatenateModules = true to open the Scope Hoisting
// webpack.config.js
module.exports = {
optimization: {
concatenateModules: true}};Copy the code
We found that the contents of hello.js are merged with the contents of index.js!
Therefore, through the function of Scope, the code files packaged by Webpack can be smaller and run faster.
Quantization using tools
- Speed-measure-webpack-plugin: can measure the time spent by various plug-ins and loaders:
npm i speed-measure-webpack-plugin -D
Copy the code
//webpack.config.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const config = {
/ /... Webpack configuration
}
module.exports = smp.wrap(config);
Copy the code
After packaging
- Webpack-bundle-analyzer: Analyses module dependencies after webpack packaging:
npm install webpack-bundle-analyzer -D
Copy the code
//webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
//....
plugins: [
/ /...
new BundleAnalyzerPlugin(),
]
Copy the code
When you start a WebPack build, a window opens by default
The DllPlugin plugin packages third-party class libraries to optimize build performance
Dll dynamic link library, is actually do cache.
DLL files are called dynamic link libraries and are often found on Windows systems. Baidu encyclopedia: baike.baidu.com/item/.dll/2…
Projects introduced many third party libraries, the libraries in a long period of time, the basic will not update, when packaged separate packaging to improve packing speed, and dynamic link library plug-in DllPlugin, its principle is to make web page depend on the basis of abstract module packaging to the DLL file, when you need to import the exists in a DLL module, The module is no longer packaged, but retrieved from a DLL.
- Dynamically linked libraries need to be compiled only once. The third-party modules used in the project have relatively stable versions, such as React and React -dom, which can be used as long as there is no need to upgrade.
Webpack already has built-in support for dynamically linked libraries:
- DllPlugin: Used to package individual dynamic link library files
- DllReferencePlugin: Used to introduce the DllPlugin plug-in packaged dynamic link library file into the main configuration file
Create a new webpack.dll.config.js file and package the base module.
We use the react and react-dom libraries in index.js, and we’ll package them first.
const path = require("path");
const { DllPlugin } = require("webpack");
module.exports = {
mode: "development".entry: {
react: ["react"."react-dom"]},output: {
path: path.resolve(__dirname, "./dll"),
filename: "[name].dll.js".library: "react"
},
plugins: [
new DllPlugin({
// Manifest.json file output location
path: path.join(__dirname, "./dll"."[name]-manifest.json"),
// Define the function name exposed by the packaged public vendor file
name: "react"}})]Copy the code
Add it in package.json
"dev:dll": "webpack --config ./webpack.dll.config.js"
Copy the code
run
npm run dev:dll
Copy the code
You’ll notice an extra DLL folder with the DLL. Js file in it, so we’ve packed our React files separately:
- DLL files contain code for a large number of modules, which are stored in an array. Using the array’s index number as the ID, you can access the module in window. XXX by exposing yourself globally with a variable.
- Manifest.json describes which modules are contained in the corresponding DLL. js, along with their ids and paths.
How do I use it next?
To build an access dynamic link library for a Web project, do the following:
- Separate the base modules that web pages depend on and package them into a single dynamic link library, which can contain multiple modules.
- If the module to be imported exists in a dynamic link library, do not package it again and use the built dynamic link library directly.
##webpack.dev.config.js
const { DllReferencePlugin } = require("webpack");
new DllReferencePlugin({
manifest: path.resolve(__dirname,"./dll/react-manifest.json")}),Copy the code
- All dynamic link libraries that the page depends on need to be loaded.
The add-asset-html-webpack-plugin is recommended to help us do this.
Install a dependency on NPM I add-asset-html-webpack-plugin, which will inject our packed DLL. js file into our generated index.html. Make changes in the webpack.base.config.js file.
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin");
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '.. /dll/react.dll.js') // The corresponding DLL file path
}),
Copy the code
It’s not hard to understand, it’s hard to operate. Fortunately, it is no longer used in Webpack5, and the HardSourceWebpackPlugin is used, which has the same optimization effect, but is extremely simple to use
- Provides intermediate cache function
- The first build didn’t change much
- The second build time is a big savings
We use the hard-source-webpack-plugin in Webpack 4:
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
const plugins = [
new HardSourceWebpackPlugin()
]
Copy the code
Use happypack to execute tasks concurrently
Webpack running on Node.js is a single-threaded model, meaning that Webpack needs to process tasks one by one, not multiple tasks at a time. Happy Pack allows Webpack to do this by splitting the task into multiple sub-processes for concurrent execution, which then send the results to the main process. Thus exerting the power of multi-core CPU computer.
It is mainly used when the construction time is long and the project is complex.
npm i -D happypack
const HappyPack = require("happypack");
// Create a shared process pool containing 5 child processes
var happyThreadPool = HappyPack.ThreadPool({ size: 5 });
// webpack.config.js
rules: [
{
test: /\.jsx? $/,
exclude: /node_modules/,
use: [
{
// Each loader corresponds to an ID
loader: "happypack/loader? id=babel",},],}, {test: /\.css$/,
include: path.resolve(__dirname, "./src"),
use: ["happypack/loader? id=css"],},]// Add to plugins
plugins: [new HappyPack({
// Use a unique identifier id to indicate that the current HappyPack is used to process a specific class of files
id: "babel".// How to handle.js files, which are used in the same way as in Loader configuration
loaders: ["babel-loader? cacheDirectory"].threadPool: happyThreadPool,
}),
new HappyPack({
id: "css".loaders: ["style-loader"."css-loader"],}),]Copy the code