Web Performance Optimization -- package build optimization

Hang Seng light cloud community 2021-09-15 09:51:45

author : Mai le

source :  Hang Seng LIGHT Cloud community

Compress and merge

The optimization points involved in resource consolidation and compression include two aspects : One is to reduce HTTP The number of requests , The other is to reduce HTTP The size of the requested resource . Now we will discuss in detail :HTML Compress 、CSS Compress 、JavaScript Compression and obfuscation and file merging .

HTML Compress

What is? HTML Compress ?

web performance optimization —— Package build optimization _Javascript

Prepare one html file Uncompressed size 6kb, Use here node Tools to compress :

var fs = require('fs');
var minify = require('html-minifier').minify;
fs.readFile('./test.htm', 'utf8', function (err, data) {
if (err) {
throw err;
}
fs.writeFile('./test_result.html',
minify(data,{
removeComments: true, // Delete Note 
collapseWhitespace: true, // Merge spaces 
minifyJS:true, // In a compressed file css
minifyCSS:true // In a compressed file js
}),
function(){
console.log('success');
}
);
});

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

The compressed content is half less , The size is 3kb,html There are no more spaces, comments, etc .

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Document</title><style>img{width:700px;height:200px;display:block}</style></head><body><ul id="uls"><li><a rel="nofollow" href="http://www.baidu.com"> Baidu </a></li></ul><img loading="lazy" src="./image/home-1.png" alt="photo"> <img loading="lazy" src="./image/home-2.png" alt="photo"> <img loading="lazy" src="./image/home-3.png" alt="photo"> <img loading="lazy" src="./image/home-4.png" alt="photo"> <img loading="lazy" src="./image/home-5.png" alt="photo"> <img loading="lazy" src="./image/home-6.png" alt="photo"> <img loading="lazy" src="./image/home-7.png" alt="photo"> <img loading="lazy" src="./image/home-8.png" alt="photo"> <img loading="lazy" src="./image/home-9.png" alt="photo"> <img loading="lazy" src="./image/home-10.png" alt="photo"> <img loading="lazy" src="./image/home-11.png" alt="photo"> <img loading="lazy" src="./image/home-12.png" alt="photo"> <img loading="lazy" src="./image/home-13.png" alt="photo"> <img loading="lazy" src="./image/home-14.png" alt="photo"> <img loading="lazy" src="./image/home-15.png" alt="photo"> <img loading="lazy" src="./image/home-16.png" alt="photo"> <img loading="lazy" src="./image/home-17.png" alt="photo"> <img loading="lazy" src="./image/home-18.png" alt="photo"> <img loading="lazy" src="./image/home-19.png" alt="photo"> <img loading="lazy" src="./image/home-20.png" alt="photo"> <img loading="lazy" src="./image/home-21.png" alt="photo"> <img loading="lazy" src="./image/home-22.png" alt="photo"> <img loading="lazy" src="./image/home-23.png" alt="photo"> <img loading="lazy" src="./image/home-24.png" alt="photo"> <img loading="lazy" src="./image/home-25.png" alt="photo"> <img loading="lazy" src="./image/home-26.png" alt="photo"> <img loading="lazy" src="./image/home-27.png" alt="photo"> <img loading="lazy" src="./image/home-28.png" alt="photo"> <img loading="lazy" src="./image/home-29.png" alt="photo"> <img loading="lazy" src="./image/home-30.png" alt="photo"><script></script><script></script></body></html>

  • 1.

css Compression of

.box {
background: red;
}

  • 1.
  • 2.
  • 3.
// mini.js
var CleanCSS = require('clean-css');
var fs = require('fs');
var minify = require('html-minifier').minify;
var options = {
};
fs.readFile('./css/index.css', 'utf8', function (err, data) {
if (err) {
throw err;
}
const a = new CleanCSS(options).minify(data)
console.log(a)
fs.writeFile('./css/test.css',
a.styles,
function(err){
if(err) {
console.log(err)
}
console.log('success');
}
);
});

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

node mini.js

css Compressed line :

.box{background:red}

  • 1.

Compress js

JavaScript The processing of this part mainly includes three aspects : Deletion of invalid characters and comments 、 Code semantic reduction and optimization and code obfuscation protection . Principle and of deleting invalid characters and comments HTML and CSS The compression is similar to , This paper mainly introduces code semantic reduction and optimization and code obfuscation protection .

Code semantic reduction and optimization

Through to JavaScript The compression of can shorten the length of some variables , For example, a long variable name is compressed , You can use very short words like a、b Instead of , This can further effectively reduce JavaScript The amount of code . It can also be optimized for some repetitive code , For example, remove duplicate variable assignments , The optimization of reducing and merging some invalid code .

Code obfuscation protection

Because any user who can access the website page , You can view the front-end through the developer tools of the browser JavaScript Code , If the semantics of the front-end code are very obvious , No compression, no confusion , Its format remains intact , So in theory, anyone visiting the website can easily pry into the logic in our code , So as to do something that threatens the security of the system . So go ahead JavaScript Code compression and obfuscation , It is also a protection for our front-end code .

How to compress ?

Get ready js file

var progress = 0;
for(var i = 0; i < 10000; i ++) {
progress = i + 9;
console.log(progress)
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
var fs = require('fs');
var uglifyJs = require('uglify-js');
var options = {
toplevel: true
};
fs.readFile('./js/file3.js', 'utf8', function (err, data) {
if (err) {
throw err;
}
const a = uglifyJs.minify(data, options)
console.log(a)
fs.writeFile('./js/test.js',
a.code,
function(err){
if(err) {
console.log(err)
}
console.log('success');
}
);
});

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

Compressed content , The space is cancelled , The name of the variable is also replaced by a simple :

{ code: 'for(var o=0;o<1e4;o++)console.log(o+9);' }

  • 1.

File merge

Suppose we have three JavaScript file , Namely a.js、b.js and c.js, When using keep-alive When the mode does not make a merge request , Its request process is shown in Figure :

web performance optimization —— Package build optimization _ front end _02

If it is a merge request , Then you only need to issue a get a-b-c.js The request can receive all the content , Pictured :

web performance optimization —— Package build optimization _web_03

benefits :

The benefits of merging files are obvious , Reduce the number of network requests , Reduced waiting time .

Disadvantage :

But it can't be said that merging files is omnipotent , There are also shortcomings .

The first is the first screen rendering , When the files are merged ,JavaScript The file size will certainly be larger than before merging , So it's going on HTML Web page rendering , If the rendering process depends on the requested JavaScript file , You must wait for the file request to load before rendering .

The second is cache invalidation , Because at present, most projects have caching strategies , That is, for each request JavaScript The file will be added with md5 The stamp of , It is used to identify whether the file has been modified or updated , When it is found that the file has been modified , Will invalidate the cache and re request the file . If there is only one minor change in the source file , Only the modified files will be invalidated if no file merge is performed , And if you merge files , It will cause a large area of cache invalidation .

Merger proposal :

Merge Public Libraries , Usually, our front-end code will contain its own business logic code and referenced third-party public library code , Business logic code changes more frequently than common library code , So you can package the common library code into a file , The business code is processed separately .

Merge business codes according to different pages . But at present, most front-end applications are single page applications , How to merge by page ? It can be packaged according to different routes , Only when accessing the corresponding route can the corresponding file be loaded .

combination webpack Package build optimization

Reduce loader The scope of implementation of

module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: 'node_modules', // Excluded modules 
include: '', // Included modules 
use: [
// [style-loader](/loaders/style-loader)
{ loader: 'bable-loader' }
]
}
]
}
};

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

If not exclude Field , be webpack All files under the path of the configuration file will be JavaScript Files use babel-loader, although babel-loader Is powerful , But execution is slow .

Because the files of the third-party library have been executed once before publishing babel-loader, There is no need to repeat , Increase unnecessary packaging and build time , therefore exclude Field cannot be omitted .

For image files, it is not necessary to pass include or exclude To reduce loader The frequency of execution , Because no matter where the picture is introduced , Finally, packaging needs to pass url-loader We're going to process it , therefore include or exclude The grammar of does not apply to all loader type , It depends on the specific situation .

Open the cache

Enable caching to cache the build results to the file system , Then you can babel-loader Our work efficiency has been multiplied .

{
loader: 'bable-loader' ,
options: {
cacheDirectory: true // Open the cache 
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

Ensure the simplicity and reliability of plug-ins

Results in the development environment , Since there is no need to consider the loading rate of the code to the user , And compression will reduce the readability of the code , Increase development costs , Therefore, there is no need to introduce code compression plug-ins in the development environment .

For cases where plug-ins are necessary , It is recommended to use webpack Plug ins recommended on the official website , Because the plug-in performance of this channel has often been officially tested , If you use a plug-in developed by an unauthenticated third-party company or individual , Although they may help us solve a corresponding problem in the packaging build process , But its performance is not guaranteed , It may cause the overall packaging speed to decrease .

The reasonable configuration resolve Parameters

resolve.extensions Configuration optimization

When we introduce JavaScript Code module , We generally do not introduce file suffixes , The usual way of writing is as follows :

import Foo from './components/Foo'

  • 1.
  • 2.

When the project scale is relatively large , To facilitate the organization and maintenance of code , Multiple module files will be split for reference , As one can imagine , It is very troublesome to fill in the file suffix every time the module is introduced , Because the file suffix of the code module is nothing more than .js, Or is it React Medium .jsx、TypeScript Medium .ts.

We can use resolve Medium extensions Property to declare these suffixes , Let the project build and package , from webpack Help us find and complete the file suffix . At the same time, the reference to the component path can also be through resolve Of alias Configuration to simplify , The configuration is as follows :

 resolve: {
extensions: ['.js', '.json'],
}

  • 1.
  • 2.
  • 3.

When you meet require(‘./data’) When such an import statement ,Webpack I'll find it first ./data.js file , If the file does not exist , Just look for ./data.json writing Pieces of , If you still can't find it, report it wrong .

If the list is longer , Or the correct suffix goes back , Will cause the number of attempts The more , therefore resolve.extensions The configuration of will also affect the performance of the build . In the configuration resolve.extensions The following points need to be observed , To optimize constructability as much as possible can .

1 Try to keep the suffix list as small as possible , Don't write the impossible situation in the project to Suffix try list .
2 The highest frequency of file suffixes should take precedence , So as to exit as soon as possible Find process .
3 When writing import statements in source code , Use suffixes as much as possible , So you can avoid looking for The process . For example, under certain circumstances require(‘./data’) It's written in require(‘./data.json’).

resolve.modules Configuration optimization

resolve.modules The default value of is [‘node_modules’], The meaning is to go to the current directory first Of ./node_modules Go to the directory and find the module we are looking for , If not , Just go to the last Level directory …/node_modules In looking for , If you don't, go to …/…/node_modules In looking for , In this way PUSH , This sum Node.js The module search mechanism of is very similar .

When the third-party modules are installed in the root directory of the project ./node_modules Under the table of contents when , There's no need to look for it layer by layer in the default way , It can be indicated that a third party The absolute path of the module , To reduce search , The configuration is as follows :

 resolve: {
...
modules: [path.resolve(__dirname, 'node_modules')],
},

  • 1.
  • 2.
  • 3.
  • 4.

resolve.alias Configuration optimization

In actual combat projects, we often rely on some huge third-party modules , With React The library is example ,

You can see what's released in React The library contains two sets of code .

1 One is to use CommonJS Standardized modular code , These files are all in lib Under the table of contents , With package.json The entry file specified in react.js It is the entrance of the module .
2 One is to React All the relevant code is packaged into a single In file , These codes are not modular , Can be executed directly . among dist/react.js use In the development environment , It contains check and warning codes .dist/react.min.js For on-line loop habitat , Minimized .

By default ,Webpack From the entry file ./node_modules/react/react.js Start recursively parsing and processing dozens of dependent files , This can be a time-consuming operation . through Over configuration resolve.alias, It can make Webpack Processing React library , Direct use of separate 、 complete react.min.js file , So as to skip the time-consuming recursive parsing operation .

resolve: {
extensions: ['.js', '.jsx'],
alias: {
'@': path.resolve(__dirname, 'src'),
react: path.resolve(__dirname, './node_modules/react/dist/react.min.js'),
},
modules: [path.resolve(__dirname, 'node_modules')],
},

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

Introduce... According to the environment :

 react: isDev ? path.resolve(__dirname, './node_modules/react/cjs/react.development.js') : path.resolve(__dirname, './node_modules/react/cjs/react.production.min.js'),

  • 1.
  • 2.

Use DllPlugin

Whenever a change occurs that requires repackaging ,webpack All referenced third-party component libraries will be analyzed by default , Finally, package it into our project code . In general , The code of the third-party component package is stable , The version remains unchanged and the content will not change .

This will use DllPlugin, It is based on Windows Dynamic link library (DLL) Created by the thought of , The plug-in will package the third-party library into a separate file , As a simple dependency Library , It will not participate in repackaging with our project code , Only when the version of the dependency itself changes will it be repackaged .

DllPlugin plug-in unit : It is used to package individual dynamic link library files .

DllReferencePlugin plug-in unit : Used to introduce... In the main configuration file DllPlugin The dynamic link library file packaged by the plug-in .

Use as follows :

webpack.dll.js

const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: "development",
entry:{
vue: ["vue"],
},
output: {
path: __dirname + "/src/dll/",
filename: '[name].dll.js',
library: 'dll_[name]'
},
optimization: {
minimize: true,
// Compress js
minimizer: [
new TerserPlugin({
parallel: true, // Start multi process compression The official advice 
})
]
},
plugins:[
new CleanWebpackPlugin(), // It's empty output Medium gain path
new webpack.DllPlugin({
path: __dirname+'/src/dll/[name].dll.json',
name: 'dll_[name]',
})
]
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

web performance optimization —— Package build optimization _HTML_04

webpack The configuration is as follows :

new webpack.DllReferencePlugin({
manifest:require('./src/dll/jquery.json')
}),
new webpack.DllReferencePlugin({
manifest:require('./src/dll/loadsh.json')
})

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

Insert html

 // Package a file and output it to , And in html Automatically import the resource in the 
new AddAssetHtmlPlugin([{
outputPath: "js/",
filepath: path.resolve(__dirname,'src/dll/vue.dll.js') // File path 
}])

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

Convert single process to multi process

We all know webpack It's single process , Even if multiple tasks exist at the same time , They can only be queued one by one , This is a nodejs The limitation of .

We can use happypack Release fully CPU Advantages in multi-core concurrency , Help us decompose the package build task into multiple subtasks to execute concurrently , This will greatly improve the efficiency of packaging .

// It is recommended to use this when there are many files , If there are only one or two, it will slow down .
const HappyPack=require('happypack');
const os=require('os'); // Get the operating system 
const happyThreadPool=HappyPack.ThreadPool({size:os.cpus().length}) // Create a new process pool 
...
{
test: /\.js$/,
loader: 'happypack/loader?id=happyBabel', // Yes js The processing uses happypack
include: [resolve('src')]
},
...
new HappyPack({
id:'happyBabel',
loaders:[
{
loader:'babel-loader?cacheDirectory=true' // Instead of babel-loader
}
],
threadPool:happyThreadPool,
verbose:true
})
...

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

Compress the volume of the packing result

Remove redundant code

webpack from 2.0 Version start , Based on ES6 Launched Tree-shaking. For example, in a component through import Two modules are introduced module1 and module2, But only used module1 Not used module2, Due to the use of reference modules , It can be identified in the process of static analysis , So when packaging goes to this component ,Tree-shaking Will directly help us module2 Delete .

The rest of the deleted code mainly occurs in the code compression phase , Some comments will be deleted , Space , Delete console etc. .

webpack4 Automatic compression of code has been supported at the beginning , Just need it mode The mode is set to production You can turn on the code compression function .

Code splits and loads on demand

With react For example , Create a project with scaffolding :

create-react-app test

cd test

npm install

npm run start

web performance optimization —— Package build optimization _web_05

Foo.js

// Foo.js
import React from 'react';
import moment from 'moment';
import { TEXT } from '../utils/index'
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.setState({
now: moment().format('YYYY-MM-DD hh:mm:ss'),
});
}
render() {
return (
<div>
Foo { this.state.now }
{TEXT}
</div>
);
}
}
export default Foo;

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

Bar.js

// Bar.js
import React from 'react';
import moment from 'moment';
import { TEXT } from '../utils/index'
class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.setState({
now: moment().format('YYYY-MM-DD hh:mm:ss'),
});
}
render() {
const { now } = this.state;
return (
<div>
Bar { now }
{ TEXT }
</div>
);
}
}
export default Bar

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

App.js

foo Components and bar Components are loaded asynchronously ,moment Is common to both components node_modules library ,TEXT Is common to both components utils The copy in the document .

npm run start Start project

web performance optimization —— Package build optimization _HTML_06

Click load Foo:

web performance optimization —— Package build optimization _Javascript_07

You can see Foo The code of the component is loaded after clicking the button , In this way, on-demand loading is realized , The first screen rendering can be optimized in combination with routing .

In addition, you can see 0.chunk.js It is the third party on which the two components depend , If you click load Bar, There will be no need to reload , Load only 1.chunk.js Can .Bar Content of the component :

web performance optimization —— Package build optimization _CSS_08

Visual analysis

1 Official version

(1)Mac webpack --profile --json > stats.json
(2)Window: webpack --profile --json | Out-file ‘stats.json’ -Encoding OEM

Then will output json Upload the file to the following website for analysis

 http://webpack.github.io/analyse/

choice stats.json You can see the detailed information in the file , Generated a visual page , Then open the Modules page , You can analyze the packaging time and size of each file

web performance optimization —— Package build optimization _ front end _09

2 Community version

npm i webpack-bundle-analyzer --save

const wba = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
plugin: [
new wba()
]

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

web performance optimization —— Package build optimization _web_10

You can more intuitively see the size and proportion of each module , And then do the corresponding optimization .

I hope the content of this article can provide you with useful help in optimizing project construction .

Please bring the original link to reprint ,thank
Similar articles

2021-09-15

2021-09-15