In this chapter, we will take a look at how to optimize Webpack.
Git address: github.com/jxs7754/dem…
1. Optimization of construction speed
- Advanced versions of Node and Webpack
- Enable multiple processes to speed up parsing and compression
- Subcontract, separate base package
- Use caching to speed up secondary builds
- Reduce file search scope
Speed analysis: use speed-measure-webpack-plugin
const SpeedMeasureWebpackPlugin = reqire('speed-measure-webpack-plugin');
const smp = new SeedMeasureWebpackPlugin();
const webpackCofig = smp.wrap({
plugins:[
// MyPlugin(),
]
})
Copy the code
You can analyze the total time of packaging and view the time of each loader and plugins.
1.1 Use older versions of Node and Webapck
- V8 engine upgrade optimization
- Webpack4 uses the faster MD4 hash algorithm by default
- Webpacks AST can be passed directly from the Loader to the AST, reducing parsing time
- Use string methods instead of regular expressions
1.2 Enabling multiple Processes
thread-loader
{
module:{
rules: [
{
test: '/.js$/',
use: [
{
loader: 'thread-loader', options:{workers: 3,}},'babel-loader'}]}}Copy the code
HappyPack(no longer maintained by author)
const HappyPack = require('happypack');
exports.module = {
rules: [{test: /.js$/.// 1) replace your original list of loaders with "happypack/loader":
// loaders: [ 'babel-loader?presets[]=es2015' ],
use: 'happypack/loader'.include: [ / *... * /].exclude: [ / *... * /]]}}; exports.plugins = [// 2) create the plugin:
new HappyPack({
// 3) re-add the loaders you replaced above in #1:
loaders: [ 'babel-loader? presets[]=es2015']})];Copy the code
Multithreaded compression
// terser-webpack-plugin module.exports = { optimization: { minimizer: { new TerserPlugin({ parallel: // Uglifyjs-webpack-plugin // uglifyjs-webpack-pluginCopy the code
1.3 the subcontract
Set Externals, and run the html-webpack-externals-plugin command to pass the basic packet (vue vue-router) through the CDN without inserting it into the packet.
new HtmlWebpackExternalsPlugin({
externals: [
{
module: 'react',
entry: 'https://xxx/react.min.js',
global: 'React',
},
{
module: 'react-dom',
entry: 'https://xxx/react-dom.min.js',
global: 'ReactDOM',}]}),Copy the code
If there is no CDN, the DllPlugin can be precompiled for subcontracting, and the DllReferencePlugin refers to manifest.json
Module. exports = {mode: exports'production',
entry: {
vue: ['vue/dist/vue.esm.js'.'vue-router'.'vuex'],
axios: ['axios'.'qs'],
// ui: ['element-ui'],
},
output: {
filename: '[name]_[chunkhash:8].dll.js',
path: path.join(__dirname, 'build'),
library: '[name]',
},
plugins: [
new CleanWebpackPlugin(),
new webpack.DllPlugin({
name: '[name]_[hash]',
path: path.join(__dirname, 'build/[name].json'),})]}; Module. exports = {plugins: [...['vue'.'axios'].map((item) => new webpack.DllReferencePlugin({
context: path.join(__dirname, './build'),
manifest: require(`./build/${item}.json`),
})),
]
}
Copy the code
1.4 the cache
Caching is used to speed up builds during secondary builds
Babel-loader enables caching
{
loader: 'babel-loader',
options:{
cacheDirectory: true}}Copy the code
Terser-webpack-plugin enables caching
{optimization: {minimizer: {new TerserPlugin({// multi-thread parallel: 4, // cache:true,})}}}Copy the code
Hard – source – webpack – plugin or cache – loader
1.5 Reduce the file search scope
Optimizing loader Configuration
Because Loader’s file conversion operation is time-consuming, it is necessary to keep as few files as possible to be processed by Loader. You can use test, include, and exclude to match the file to which the Loader applies the rule.
Use reasonable aliases
In practical projects, some huge third-party modules are often relied on. Take Vue library as an example, the released Vue library contains multiple sets of code, while Vue.Runtime.esm.js only contains runtime code. If you don’t use the template option you can use this to reduce the packing volume.
module.exports = {
resolve: {
alias: {
'vue$': 'vue/dist/vue.runtime.esm.js',}}}Copy the code
Optimize the resolve.modules configuration
Resolve. Modules defaults to [‘node_modules’], which means to find the module in the node_modules directory of the current directory. If not, go to the upper directory.. /node_modules, then go to.. /.. /node_modules, and so on. When installed third-party modules are placed in the node_modules directory of the project root directory, there is no need to follow the default way of searching layer by layer. You can specify the absolute path to store third-party modules to reduce searching.
module.exports = {
resolve: {
modules: [path.resolve( __dirname,'node modules')]}}Copy the code
Optimize the resolve.mainfields configuration
There is a package.json file that describes the properties of the module in installed third-party modules. Multiple field description entry files can exist because some modules can be used in multiple environments at the same time, requiring different code for different runtime environments. Segmentfault.com/a/119000001…
Optimize the resolve.extensions configuration
When an import statement does not have a file suffix, Webpack automatically applies the suffix to try to ask if the file exists. The longer the list is, or the later the correct suffix is, the more attempts will be made, so the configuration of the resolve.extensions will also affect the performance of the build.
- Keep the suffix list as small as possible
- File suffixes with high frequency are given priority
- Source code to write import statements, as far as possible with suffixes
{
extensions: ['.js'],},Copy the code
2. Build volume optimization
- Extract common code, split code, load on demand, lazy load
- tree-shaking
- scope-hoisting
- Delete unnecessary CSS
- Dynamic polyfill
- Code compression, enable Gzip compression
Volume analysis Webpack-bundle-Analyzer
You can analyze the size of dependent third-party modules and the code size of components within the business
const BoundAnalysisPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BoundAnalysisPlugin(),
]
}
Copy the code
2.1 Extract common code, split code, load on demand, lazy load
// The component loads import {Button} from on demand'element-ui'; // modules load import {on demandcloneDeep} from 'lodash-es'; // Vue route lazy loading const Foo = () => import(/* webpackChunkName:"group-foo"* /'./Foo.vue')
Copy the code
optimization: {
splitChunks: {
chunks: 'all',
minSize: 30000,
minRemainingSize: 0,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 6,
maxInitialRequests: 4,
automaticNameDelimiter: '~',
automaticNameMaxLength: 30,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true}}}}Copy the code
2.2 the tree – shaking
A module may have multiple methods, and as long as one of them is used, the whole file will be sent to the bundle. Tree shaking is only putting used methods into the bundle, and unused methods will be erased during the Uglify phase. Matters needing attention:
- Mode: production Default Babel set modules:false,
- ES6 syntax must be used
2.3 Deleting unnecessary CSS
Use purgecss-Webpack-Plugin with mini-CSS-extract-Plugin
const config = {
module:{
rules: [
{
test: '/.scss$/',
use: [
MiniCssExtractPlugin.loader,
'css-loader'.'sass-loader',
{
loader: 'postcss-loader', options: {plugins: () => [// automatic extension CSS require('autoprefixer')(),
],
},
},
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: getAssetPath(
`css/[name]_[contenthash:8]'}.css`, ), }), new PurgecssPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), }), ] }Copy the code
2.4 dynamic polyfill
plan | advantages | disadvantages |
---|---|---|
babel-polyfill | Instead of | Volume is too big |
@babel/plugin-transform-runtime | Polyfill only uses methods and classes that are small in size | Cannot polyfill the method on the prototype |
polyfill-service | Only the polyfills required by the customer are returned | Weird Browsers in China |
2.5 the Scope – Hoisting
ModuleConcatenationPlugin webpack4 now has support mode is not equal to none
2.6 Image compression, code compression, can also enable Gzip compression
Use image-webpack-loader for image compression
3. Load optimization
3.1 preload
Use @vue/preload-webpack-plugin for code preloading
const config = {
plugins: [
new PreloadPlugin({
rel: 'preload',
include: 'initial',
fileBlacklist: [/\.map$/, /hot-update\.js$/],
}),
new PreloadPlugin({
rel: 'prefetch',
include: 'asyncChunks',]}})Copy the code
3.2 Use file fingerprint and browser cache
- Hash: Relates to the construction of the entire project. The Hash value for the entire project will change whenever the project file is modified
- Chunkhash: Related to chunks packed by Webpack, different entries will generate different Chunkhash values
- Contenthash: Defines hash based on the file content. Contenthash does not change if the file content is not changed
// js
{
output: {
filename: '[name]_[chunkhash:8].js'
}
}
// css
// MiniCssExtractPlugin
{
plugins:[
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'})]} // image // file-loader is usedhash(where thehashMd5) {module:{rules:[{test: /\.(png|svg|jpg|gif)$/,
use: [{
loader: 'file-loader ', options: {name: 'img/[name][hash:8].[ext] '}}]}}Copy the code
. To be continued