This paper is participating in thePerformance optimization combat record”Topic essay activity
preface
Hi everyone, I’m YK bacteria, front-end dish chicken. Most of my blog posts are based on notes, but I also have some summaries of my own, and this one is the same. In fact, this is also my notes a long time ago, today to share with you, is also B station still silicon Valley’s latest webpack video part of the notes. This article has been in my nuggets draft box, was also ready to take out when small knowledge hair, just nuggets to performance optimization activities, catch a coincidence, hair this ~ sorry everybody, I water water water water an activity article ~
BB two sentences again
Every time I watch some videos, I will blog my notes, and then leave a link of my notes in the comments section. I have been taking notes on another platform before, so I transfer them. Basically, I call all platforms YK bacteria. Where to write bad place welcome message criticism and correction.
The text start
Last time we briefly introduced the basic use of WebPack, today we will take a look at some configuration using WebPack for performance optimization, although this article only briefly listed some concepts and basic configuration use, but will give you a general understanding of performance optimization, later browsing the documentation will be much easier
1. What is WebPack performance optimization
- Development environment performance optimization
- Production environment performance optimization
1.1 Development environment performance optimization
- Optimize packaging build speed
- HMR
- Optimized code debugging
- source-map
1.2 Optimization of production environment performance
- Optimize packaging build speed
- oneOf
- Babel cache
- Multi-process packaging
- externals
- dll
- Optimize the performance of code execution
- Cache (hash – chunkhash – contenthash)
- tree shaking
- code split
- Lazy loading/preloading
- pwa
2. Development environment performance optimization
2.1 HMR
Hot Module replacement Hot module replacement
target: "web".devServer: {
// Enable HMR
// When the WebPack configuration is changed, the webpack service must be restarted for the new configuration to take effect
hot: true,}Copy the code
- What it does: When a module changes, it repackages only that module (not all modules) [greatly improves build speed]
-
Style files: you can use the HMR function because style-loader is implemented internally
-
Js file: HMR function cannot be used by default –> Need to modify js code, add code supporting HMR function
Note: the HMR function can only handle other files that are not entry JS files.
Add it to index.js
if (module.hot) {
// Once module.hot is true, HMR is enabled. --> Make the HMR function code work
module.hot.accept("./print.js".function () {
// The print.js method listens for changes in the print.js file, and when changes occur, other modules are not repackaged and built.
// The following callback functions are executed
print();
});
}
Copy the code
- HTML files: the HMR function is disabled by default. It also causes problems: HTML files cannot be hot updated.
[“./ SRC /js/index.js”, “./ SRC /index.html”]
2.2 the source – the map
Source-map: a technique that provides source-to-build-code mapping (mapping to trace source code errors if errors are made after a build)
devtool: 'source-map'
Copy the code
The options behind devtool can be filled with:
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
Copy the code
code | location | note |
---|---|---|
source-map |
external | Error code exact information and source code error location |
inline-source-map |
inline | Only one inline source-map is generated Error code exact information and source code error location |
hidden-source-map |
external | Error code error cause, but no error location You cannot trace source code errors, only the error location of the built code |
eval-source-map |
inline | Each file generates a corresponding source-map, all in eval Error code exact information and source code error location |
nosources-source-map |
external | Error code exact information, but no source code information |
cheap-source-map |
external | Error code exact information and source code error location Only accurate lines |
cheap-module-source-map |
external | Error code exact information and source code error location Module adds the Loader source map |
The difference between inline and external
- External generated file, inline not
- Inline builds are faster
[Development environment] : Fast speed, more friendly debugging
- Speed (eval > the inline > being >…).
eval-cheap-souce-map
eval-source-map
- More debug friendly
souce-map
cheap-module-souce-map
cheap-source-map
- Recommended use –>
eval-source-map
/eval-cheap-module-souce-map
[Production environment] : Should source code be hidden? Debugging should be more friendly
Inlining makes code bigger, so it’s not used in production
Nosource-source-map Is hidden
Hidden-source-map only hides the source code, prompting post-build code error messages
Source-map/cheap-module-souce-map is recommended
3. Optimized production environment performance
3.1 oneOf
Only one loader will match after using oneOf
Note: you can’t have two configurations that handle the same type of file, js needs to bring esLint out
module: {
rules: [{// In package.json eslintConfig --> Airbnb
test: /\.js$/,
exclude: /node_modules/.// Priority execution
enforce: "pre".loader: "eslint-loader".options: {
fix: true,}}, {// The following loader will only match one
// Note: No two configurations can handle the same type of file
oneOf: [{test: /\.css$/,
use: [...commonCssLoader],
},
{
test: /\.less$/,
use: [...commonCssLoader, "less-loader"],},/* Normally, only one loader can process a file. When a file is to be processed by multiple Loaders, it is important to specify the order in which loader executes: esLint before Babel */
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader".options: {
presets: [["@babel/preset-env",
{
useBuiltIns: "usage".corejs: { version: 3 },
targets: {
chrome: "60".firefox: "50",},},],],},}, {test: /\.(jpg|png|gif)/,
loader: "url-loader".options: {
limit: 8 * 1024.name: "[hash:10].[ext]".outputPath: "imgs".esModule: false,}}, {test: /\.html$/,
loader: "html-loader"}, {exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: "file-loader".options: {
outputPath: "media",},},],},Copy the code
3.2 the cache
3.2.1 Babel cache
Add the configuration cacheDirectory: true to options in babel-loader to make the second package build faster
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader".options: {
presets: [["@babel/preset-env",
{
useBuiltIns: "usage".corejs: { version: 3 },
targets: {
chrome: "60".firefox: "50",},},],// Enable the Babel cache
// On the second build, the previous cache is read
cacheDirectory: true,}}Copy the code
3.2.2 File Resource Caching
Forced cache, in a certain period of time always go to the cache, but temporarily change a resource, resulting in page update is not timely, with the cache, so to package out of the file name behind a hash value, so change the file content, the file name will change
Hash: A unique hash value is generated for each Wepack build
- Problem: Because JS and CSS use the same hash value
- If repackaged, it invalidates all caches (maybe I only changed one file)
Chunkhash: Hash value generated by chunk. If the package comes from the same chunk, the hash value is the same
- Problem: JS and CSS have the same hash value
- Because CSS was introduced in JS, it belongs to the same
chunk
Contenthash: Generates hash values based on file contents
- Different files must have different hash values
- Make the code run online and cache easier to use
output: {
filename: "js/built.[contenthash:10].js".path: resolve(__dirname, "build"),},plugins: [
new MiniCssExtractPlugin({
filename: "css/built.[contenthash:10].css",})]Copy the code
3.3 the tree shaking
Tree shaking: Remove useless code
- Prerequisites: 1. ES6 modularization must be used. 2
- Effect: Reduces code size
Configure in package.json
- “SideEffects “: false all code has no sideEffects
- Problem: CSS / @babel/polyfill files may dry out
- Solution: sideEffects: [“.css”, “.less”]
3.4 code split
Split code: Split a large file into multiple files [load on demand]
Way one more entrance
Test.js is no longer needed in index.js
/ / a single entrance
// entry: './src/js/index.js',
entry: {
// Multiple entries: There is one entry, and the final output has a bundle
index: "./src/js/index.js".test: "./src/js/test.js",},output: {
// [name] : Indicates the name of the file
filename: "js/[name].[contenthash:10].js".path: resolve(__dirname, "build"),}Copy the code
Methods two optimization
Add a configuration item to package the node_modules code as a separate chunk of final output
/* 1. The node_modules code can be packaged as a separate chunk of the final output 2. Automatic analysis of multi-entry chunks, whether there are public files. If so, it will be packaged into a single chunk */
optimization: {
splitChunks: {
chunks: 'all'}}Copy the code
For example, I’ve introduced jquery (which would otherwise be merged with index.js) to automatically analyze multiple entry chunks for common files. If there is, it will be packaged into a single chunk
It outputs three chunks
Mode three JS code dynamic loading
Chunk import dynamic import syntax: it is possible to package a file separately by using JS code
Test.js is introduced in index.js
/* Dynamic import syntax: it is possible to package a file separately */
import(/* webpackChunkName: 'test' */'./test')
.then(({ mul, count }) = > {
// The file is successfully loaded ~
console.log(mul(2.5));
})
.catch(() = > {
console.log('File load failed ~');
});
Copy the code
3.5 Lazy loading and preloading
Example: clicking a page button to call a JS file should load the js file after clicking
Each JS file is introduced only after the button is clicked so that lazy loading can be achieved
-
Lazy loading: files are loaded when they are needed
-
Prefetch: js files will be loaded before they are used.
-
Normal loading can be considered parallel loading (loading multiple files at the same time)
-
Prefetch: Surreptitiously loads resources after other resources have loaded and the browser is free
document.getElementById('btn').onclick = function() {
// Lazy loading ~ : files are loaded only when they are needed
// Prefetch: preloads the JS file before it is used
// Normal loading can be considered parallel loading (multiple files are loaded at the same time)
// Prefetch: Wait until other resources are loaded and the browser is free, then secretly load the resources
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) = > {
console.log(mul(4.5));
});
};
Copy the code
3.6 PWA
PWA: Progressive Web development application (offline access)
workbox –> workbox-webpack-plugin
npm i workbox-webpack-plugin
- Helps ServiceWorker start quickly
- Delete the old ServiceWorker
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
module.exports = {
plugins: [
new WorkboxWebpackPlugin.GenerateSW({
2. Delete the old ServiceWorker and generate a Serviceworker profile ~ */
clientsClaim: true.skipWaiting: true}})]Copy the code
Then configure it in the entry file index.js
/ / register serviceWorker
// Handle compatibility issues if supported, if not not used
if ('serviceWorker' in navigator) {
window.addEventListener('load'.() = > {
navigator.serviceWorker
.register('/service-worker.js')
.then(() = > {
console.log('Sw registered successfully ~');
})
.catch(() = > {
console.log('SW registration failed ~');
});
});
}
Copy the code
-
Eslint does not recognize window, navigator global variables
Solution: eslintConfig in package.json needs to be modified
"env": {
"browser": true Browser-side global variables are supported
}
Copy the code
-
The SW code must run on the server
–> nodejs
–> NPM I serve-g serve-s build Start the server and expose all resources in the build directory as static resources
This way, after the first load, you can cache it, and you can still access the page content after you go offline
3.7 Packaging Multiple Processes
npm i thread-loader
Copy the code
Enable multi-process packaging.
Process startup takes about 600ms and process communication is also overhead. Only when the work consumes a long time, it is necessary to package the multi-process.
{
test: /\.js$/,
exclude: /node_modules/,
use: [
/* Enable multi-process packaging. Process startup takes about 600ms and process communication is also overhead. Multi-process packaging is required only if the work takes a long time
// "thread-loader",
{
loader: "thread-loader".options: {
workers: 2.// Process 2}, {},loader: "babel-loader".options: {
presets: [["@babel/preset-env",
{
useBuiltIns: "usage".corejs: { version: 3 },
targets: {
chrome: "60".firefox: "50",},},],// Enable the Babel cache
// On the second build, the previous cache is read
cacheDirectory: true,},},],}Copy the code
3.8 externals
Prevents packages from being packaged and exported to the final bundle
externals: {
// Refuse to package jQuery
jquery: 'jQuery'
}
Copy the code
Jquery is introduced via CDN links in index.html
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
Copy the code
3.9 DLL
Use DLL technology for some libraries (third-party libraries: jquery, React, vue…) Pack separately
DLL is packaged once integrated together, later do not need to be packaged.
CDN externals (externals)
webpack.dll.js
const { resolve } = require("path");
const webpack = require("webpack");
module.exports = {
entry: {
[name] --> jquery
// ['jquery'] --> The library is jquery
jquery: ["jquery"],},output: {
filename: "[name].js".path: resolve(__dirname, "dll"),
library: "[name]_[hash]".// What is the name of the contents exposed in the packaged library
},
Jquery_hash = jquery_hash = jquery_hash = jquery.js
plugins: [
// Package to generate a manifest.json --> provide and jquery mapping
new webpack.DllPlugin({
name: "[name]_[hash]".// The exposed content name of the mapping library
path: resolve(__dirname, "dll/manifest.json"), // Output file path}),].mode: "production"};Copy the code
When you run Webpack, the default is to look for the webpack.config.js configuration file
Requirements: You need to run the webpack.dlL. js file
–> webpack --config webpack.dll.js
webpack.config.js
webpack.DllReferencePlugin
Tell WebPack which libraries are not included in the package and change their names when used
Download the plugin
npm i add-asset-html-webpack-plugin
Package a file and export it, and automatically import the resource in HTML
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
entry: './src/index.js'.output: {
filename: 'built.js'.path: resolve(__dirname, 'build')},plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
// Tell Webpack which libraries are not included in the package, and change their names when used
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')}),// Package a file and import the resource automatically in HTML
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')})],mode: 'production'
};
Copy the code
4. To summarize
Review the
Development environment performance optimization
- Optimize packaging build speed
- HMR
- Optimized code debugging
- source-map
Production environment performance optimization
- Optimize packaging build speed
- oneOf
- Babel cache
- Multi-process packaging
- externals
- dll
- Optimize the performance of code execution
- Cache (hash – chunkhash – contenthash)
- tree shaking
- code split
- Lazy loading/preloading
- pwa