background
Because the company’s front-end project takes an absurdly long time in packaging and construction, about 5min for cold startup, 5min for local construction and packaging, and 16~20min for the whole process of online Tencent cloud Docker construction and packaging. Therefore, not only greatly affects the development efficiency, but also greatly delays the time for test delivery.
Optimization idea
- Understand the project’s current WebPack configuration
- Build-dependent optimization
- Optimization of package volume correlation
- Docker-related optimizations
Quantitative tools
speed-measure-webpack-plugin
Introduction:
speed-measure-webpack-plugin npm
The first step to optimising your webpack build speed, is to know where to focus your attention. This plugin measures your webpack build speed, giving an output like this:
Through the analysis of SMP output, we can clearly understand the time spent by loader and plugin in each stage of WebPack construction.
Usage:
# Yarn
yarn add -D speed-measure-webpack-plugin
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
module.exports = {
chainWebpack: config= > {
config
.plugin('speed-measure-webpack-plugin')
.use(SpeedMeasurePlugin)
.end()
}
}
Copy the code
Other uses in this project will report error, but the above usage does not seem to distinguish between the use of plugin and Loader, and there is no information about the use of other plugins, confusing.
webpack-bundle-analyzer
Introduction:
Webpack-bundle-analyzer NPM is used to analyze webAPCK build packed files, such as subcontract, occupied volume and other parameters.
Usage:
# Yarn
yarn add -D webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
Copy the code
But in vue-CLI you can call the report command directly, and then go to the dist package directory and open report.html.
vue-cli-service build --report
or
vue-cli-service build --report-json
Copy the code
View the current Webpack configuration of VUE-CLI
introduce
Vue-cli scaffolding has a lot of the default behavior of WebPack, so we need to know what webPack is currently configured for vue-CLI based projects so we can analyze and optimize accordingly.
Vue-cli-service exposes the inspect command for reviewing the parsed Webpack configuration. The global vue executable also provides the inspect command, which simply proxies vue-cli-service inspect into your project.
Usage:
Vue inspect --mode production > output.js vue inspect --mode production > output.jsCopy the code
NPM install -g vue-cli NPM install -g vue-cli
Build-dependent optimization
hard-source-webpack-plugin
introduce
hard-source-webpack-plugin npm
HardSourceWebpackPlugin is a plugin for webpack to provide an intermediate caching step for modules. In order to see results, you’ll need to run webpack twice with this plugin: the first build will take the normal amount of time. The second build will be signficantly faster.
Caches are generated for the project when it is started, and if there are no package or other changes to the project, the next time you don’t have to spend time rebuilding, you just reuse the cache.
Usage:
#yarn
yarn add -D hard-source-webpack-plugin
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
module.exports = {
configureWebpack: config= > {
config.plugin.push(
// Provide an intermediate cache for the module. The cache path is node_modules/. Cache /hard-source
// solve Configuration changes are not being detected
new HardSourceWebpackPlugin({
root: process.cwd(),
directories: [].environmentHash: {
root: process.cwd(),
directories: [].files: ['package.json'.'yarn.lock'.'vue.config.js']}})// The main reason for configuring files is to solve the problem that the cache does not take effect due to configuration updates. Plugin will rebuild part of the cache after configuration changes)}}Copy the code
Note:
Could not freeze : Cannot read property ‘hash’ of undefined after node_modules/. Cache is deleted and the project is restarted.
Narrow the file retrieval parsing scope
To avoid useless retrieval and recursive traversal, you can use alias to specify the module to reference, and noParse does not parse third-party dependencies that do not depend on native code.
// Define the getAliasPath method to convert relative paths to absolute paths
const getAliasPath = dir= > join(__dirname, dir)
module.exports = {
configureWebpack: config= > {
config.module.noParse = /^(vu|vue-router|vuex|vuex-router-sync|lodash|echarts|axios|element-ui)$/
}
chainWebpack: config= > {
// Add an alias
config.resolve.alias
.set(The '@', getAliasPath('src'))
.set('assets', getAliasPath('src/assets'))
.set('utils', getAliasPath('src/utils'))
.set('views', getAliasPath('src/views'))
.set('components', getAliasPath('src/components'))}// EsLint is disabled in production
lintOnSave:! process.env.NODE_ENV ! = ='production',}Copy the code
Reduce packing volume
Image-webpack-plugin Image compression
The image pixel requirements are not very extreme, this compression can still be used, the compression rate looks like the naked eye is not much difference. Notice here that I did not compress SVG because when compressed SVG is packaged as Base64 at build time, the resulting Base64 will have problems and be inaccessible.
module.exports = {
chainWebpack: config= > {
// Compress the image
config.module
.rule('images')
.test(/\.(png|jpe? g|gif)(\? . *)? $/)
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({ bypassOnDebug: true })
.end()
}
}
Copy the code
UglifyJsPlugin Deletes the console comment
UglifyJsPlugin is used to compress JS files and reduce the size of JS files. It will slow down the compilation of WebPack. It is recommended that you turn it off in development and turn it on in production. It is recommended to standardize the code of team members to solve.
#yarn
yarn add -D uglifyjs-webpack-plugin
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
configureWebpack: config= > {
config.plugin.push(
new UglifyJsPlugin({
uglifyOptions: {
// Delete comments
output: {
comments: false
},
// Delete console debugger delete warning
compress: {
warnings: false.drop_console: true.//console
drop_debugger: false.pure_funcs: ['console.log'] / / remove the console}},sourceMap: false.parallel: true // Use multiple processes running in parallel to speed up builds. Default number of concurrent runs: os.cpus().length-1.}}}))Copy the code
DLL dynamic link library
This plugin creates a DLL -only bundle(DLL-only -bundle) in an additional separate Webpack setup. The plugin generates a file named manifest.json, which is used to map the DLLReferencePlugin to related dependencies.
This can be simply interpreted as separating dependencies from the project’s bundle and loading them with requests through mapping relationships. I think once it’s broken down it’s not parsed in the project, so it helps build, volume.
Configuring the DllPlugin can be divided into the following steps:
- Create a new webpack.dll.config.js file (any other name can be used) and configure the plug-in to be split.
- Create a new command in the package.json file to package specifically,
"build:dll":"webpack --config webpack.dll.config.js"
; Run the command. - Configure it in the vue.config.js file
DllReferencePlugin
, mainly reference DLLS to dependencies that need to be precompiled; - Manually introduce split bundles in index. HTML (preferably in CDN)
Installation:
#yarn
yarn add webpack-cli@^3.23. add-asset-html-webpack-plugin@^3.13. clean-webpack-plugin@^1.01. --dev
Copy the code
// webpack.dll.config.js
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path')
const webpack = require('webpack')
const CleanWebpackPlugin = require('clean-webpack-plugin')
// Directory for storing DLL files
const dllPath = 'public/vendor'
module.exports = {
entry: {
// The library file to extract
vendor: ['vue'.'vue-router'.'vuex'].utils: ['axios'.'lodash']},output: {
path: path.join(__dirname, dllPath),
filename: '[name].dll.js'.// Global variable name exposed in vendor.dll.js
// Keep the same name as webpack.dllPlugin
library: '[name]_[hash]'
},
plugins: [
// Clear the previous DLL file
new CleanWebpackPlugin(['*. *'] and {root: path.join(__dirname, dllPath)
}),
// manifest.json describes what the dynamic link library contains
new webpack.DllPlugin({
path: path.join(__dirname, dllPath, '[name]-manifest.json'),
// keep the same name as output.library
name: '[name]_[hash]'.context: process.cwd()
})
]
12
Copy the code
Used in the vue.config.js plugin
config.plugin.push(
new DllReferencePlugin({
context: process.cwd(),
manifest: require('./public/vendor/vendor-manifest.json')}),new DllReferencePlugin({
context: process.cwd(),
manifest: require('./public/vendor/utils-manifest.json')}),// Inject the DLL into the generated HTML template
new AddAssetHtmlPlugin({
// DLL file location
filepath: getPath('./public/vendor/*.js'),
// DLL reference path
publicPath: './vendor'.// DLL final output directory
outputPath: './vendor'}))Copy the code
SplitChunks split code
split-chunks-plugin webpack
- Chunks: indicates which code needs to be optimized. There are three optional values: Initial (initial chunks), async(chunks loaded on demand), and all(chunks loaded on demand). The default is Async
- MinSize: indicates the minimum module size before compression. The default value is 30000
- MinChunks: indicates the number of times to be referenced. The default value is 1
- MaxAsyncRequests: Maximum number of parallel requests that can be loaded on demand (default: 5)
- MaxInitialRequests: The maximum number of parallel requests for an entry. Default is 3
- AutomaticNameDelimiter: Named connector
- Name: indicates the name of the split block. By default, the block name and hash value are automatically generated
- CacheGroups: cacheGroups. The attributes of the cache group include test, Priority, and reuseExistingChunk
- Test: Used to control which modules are matched by this cache group
- Priority: priority of cache group packaging
- ReuseExistingChunk: No new block is created if the current block contains a module that already exists
config.optimization = {
runtimeChunk: 'single'.splitChunks: {
chunks: 'all'.// Indicates which code needs to be optimized. There are three optional values: initial(initial block), async(load on demand block), and all(all block). The default is async
maxInitialRequests: Infinity.// Maximum number of parallel requests when loading on demand. Default is 5
minSize: 30000.// Dependency packages exceeding 300000bits will be packaged separately
/ / cache group
// priority: priority of cache group packaging
// minChunks: indicates the number of times to be referenced. The default value is 1
cacheGroups: {
// Public module
commons: {
name: 'chunk-commons'.test: resolve('src'), // can customize your rules
minSize: 100.// The size exceeds 100 bytes
minChunks: 3.// minimum common number
priority: 5.reuseExistingChunk: true
},
// Third-party libraries
libs: {
name: 'chunk-libs'.test: /[\\/]node_modules[\\/]/,
priority: 10.chunks: 'initial'.// only package third parties that are initially dependent
reuseExistingChunk: true.enforce: true
},
echarts: {
name: 'chunk-echarts'.test: /[\\/]node_modules[\\/]echarts[\\/]/,
chunks: 'all'.priority: 12.reuseExistingChunk: true.enforce: true}}}}Copy the code
conclusion
The above are some small optimizations I have made in the company’s project, which may not be applicable to other projects or even not optimal in use, but they are still helpful to this project to a large extent. For more webpack optimization ideas, I attached a mind map. (For learning only, record 📝)
other
Vue CLI
Vue-configurewebpack Settings in the CLI
How do I deploy a front-end project using Docker