preface
I have read a lot of articles about packaging optimization, many of which are based on the native WebPack configuration and modify the configuration directly in the webpack.config.js file. However, the vue-cli created project already encapsulates the basic Webpack configuration and needs to modify the preset Webpack configuration in the vue.config.js file. It’s rare to see any articles on this subject, so keep a record of your practice and any potholes you’ve stepped on.
Version of this technology:
- Vue: 2.6.10
- @ vue/cli: 4.0.5
- Webpack: 4.31.0
Vue – webpack in cli
To optimize the project, we first need to know what optimizations vue-CLI has done for us, which means we need to see what options webPack has configured. Using Vue Inpsect to output webpack configurations, you can also specify the file to output: vue inspect > output.js.
Vue-cli provides two ways to change the Webpack configuration: 1. Native configuration. The results of the configuration will be merged into the final Webpack configuration by webpack-merge.
// vue.config.js
module.exports = {
configureWebpack: {
// Write webPack configuration items directly here...}}Copy the code
2. Chain-like configuration. The plug-in Webpack-chain is used to maintain webpack configuration inside VUe-CLI, which is recommended by the official because it can control its internal configuration in a more fine-grained way.
// vue.config.js
module.exports = {
chainWebpack: config= > {
config.resolve.alias.set('@assets', resolve(`src/assets`)); }},Copy the code
These two methods can be used together. For the sake of simplicity and less potholes, this optimization mainly uses the native WebPack configuration, that is, the configureWebpack method. The optimization process is divided into packaging volume optimization and packaging speed optimization.
Optimize packaging volume
Analyze the package volume using Webpack-bundle-Analyzer
Webpack officially provides a number of plug-ins to analyze packaging performance.
- Webpack-chart: Webpack Stats interactive pie chart.
- Webpack-visualizer: Visualize and analyze your bundle, checking which modules take up space and which might be reused.
- Webpack-bundle-analyzer: A plugin and CLI tool that displays bundle contents as a convenient, interactive, and scalable tree.
- Webpack Bundle Optimize Helper: This tool will analyze your bundle and suggest operational improvements to reduce the bundle size.
We use Webpack-bundle-Analyzer to analyze the package volume.
// yarn add analyze-webpack-plugin --dev
// vue.config.js
const AnalyzeWebpackPlugin = require('analyze-webpack-plugin')
module.exports = {
configureWebpack: {
plugins: [
new AnalyzeWebpackPlugin({}),
],
}
}
Copy the code
Run the yarn build command. The analysis result page is displayed.
Webpack-bundle-analyzer uses three metrics to measure package volume:
- Stat: The size of the input file, which has not been converted by, for example, compression.
- Parsed: The size of the output file, the size of the code after uglization and compression.
- Gzip: the size after gZIP compression is enabled.
Optimize moment — ContextReplacementPlugin
If you look at the figure above, you can see that moment occupies a large proportion, mainly some localized language packs, which are packaged by default. For normal applications, we only need the Chinese language pack.
Before optimization:
- Stat: 540.76 KB
- Parsed: 234.36 KB
- Gzipped: 68.46 KB
First select the appropriate language pack to set the locale:
import moment from 'moment';
import 'moment/locale/zh-cn';
moment.locale('zh-cn');
Copy the code
The function of the ContextReplacementPlugin is to change the packaging context of a module by changing the regex to make WebPack pack only the files we want.
// yarn add webpack --dev
// vue.config.js
const webpack = require('webpack');
module.exports = {
configureWebpack: {
plugins: [
new webpack.ContextReplacementPlugin(
/moment[/\\]locale$/.// This parameter indicates the packaging context we want to change
/zh-cn/ // This parameter indicates that we only want to package the file that matches the re)]}};Copy the code
After the optimization:
- Stat: 150.79 KB
- Parsed: 54.61 KB
- Gzipped: 17.96 KB
The original language pack of 393.36KB is only 3.39KB after retaining Chinese.
The community offers a number of ways to package the moment, and there are a few other options available: github.com/jmblog/how-…
Optimize the XLSX
We used this library in our project to generate excel and download it. Import {utils, writeFile} from ‘XLSX ‘; It is very bulky when packed.
Before optimization:
- Stat: 1.23 MB
- Parsed: 920.85 KB
- Gzipped: 327.65 KB
Later, the solution was found in the issue area, and only the mini version was introduced:
import { utils, writeFile } from 'xlsx/dist/xlsx.mini.min.js';
Error if you are using typescript:
Declare the following module:
// modules.d.ts
declare module 'xlsx/dist/xlsx.mini.min.js';
Copy the code
After optimization, it was reduced to almost a fraction:
- Stat: 236.73 KB
- Parsed: 189.66 KB
- Gzipped: 60.79 KB
Note that the official explanation for the size of XLSX is that it involves reading files and supports older formats. You can use this mini version if your project is just generating Excel and does not involve reading files; If it involves reading an Excel file, just do it. In the future, officials may offer a lightweight version that supports only modern file formats.
Lodash packaging volume — LoDash specific plugin
Before optimization:
- Stat: 540.17 KB
- Parsed: 73.29 KB
- Gzipped: 25.74 KB
Two plug-ins are required:
-
Babel-plugin-lodash is used to streamline the lodash module, keeping only the methods used.
-
Lodash-webpack-plugin this plugin makes the packaging smaller by replacing some of the module’s features with noop, identity, or other simpler alternatives. Note: This plugin turns off some features that are not commonly used in Lodash by default. You can turn some features on by passing options to the plugin.
These two plug-ins are used together to maximize the effect. Just add lodash to the Babel plugin and add a plugin to the WebPack configuration:
// yarn add babel-plugin-lodash lodash-webpack-plugin --dev
// babel.config.js
modules.exports = {
// Other configurations omitted...
plugins: ['lodash']}// vue.config.js
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
module.exports = {
// ...
configureWebpack: {
plugins: [
new LodashModuleReplacementPlugin()
]
}
}
Copy the code
After the optimization:
- Stat: 56.42 KB
- Parsed: 11.34 KB
- Gzipped: 3.81 KB
A lot of low-key, looking for a long time to find XD
Extract common code — splitChunks (commonChunkPlugin before WebPack4, splitChunks after WebPack4)
We used watermelon Player for our project, and found that XgPlayer, as a third-party library, was not packaged into chunk-vendors, and was also packaged twice.
For this XgPlayer, the reference situation is that there are two pages that reference a common component that references XgPlayer. So why isn’t XgPlayer packaged with chunk-vendors?
Take a look at the vue-CLI default Webpack configuration:
// ...
optimization: {
minimizer: [
// ...].splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors'.test: /[\\/]node_modules[\\/]/.priority: - 10.chunks: 'initial'
},
common: {
name: 'chunk-common'.minChunks: 1.priority: - 20.chunks: 'initial'.reuseExistingChunk: true}}}}Copy the code
Vendors package third-party libraries within node_modules that meet the criteria, which is chunks: ‘initial’. Chunks represent the type of chunks to be packaged and have three values:
- Initial: The initial chunk, which needs to be loaded immediately, is the module imported synchronously through import in main.ts.
- Async: Chunks that are introduced dynamically, such as import(), i.e. asynchronous modules that are introduced on demand.
- All: Contains synchronous and asynchronous modules. Selecting this will package all modules that test matches, in this case node_modules, which is obviously inappropriate because some third-party libraries may be used later, such as XgPlayer in this case.
So while XgPlayer was imported synchronously via import, the two page components that referenced it were imported as needed by import() in the routing file, and xgPlayer wasn’t introduced in main.ts, so it wasn’t naturally packaged into chunk-vendors.
So it should be packaged as an asynchronous module async or all.
// vue.config.js
module.exports = {
// ...
configureWebpack: {
optimization: {
splitChunks: {
cacheGroups: {
xgplayer: {
name: 'xgplayer'.test: /[\\/]node_modules[\\/]xgplayer[\\/]/.minSize: 0.minChunks: 1.reuseExistingChunk: true.chunks: 'all'
}
}
}
}
}
}
Copy the code
After optimization, only one copy was packaged:
Optimize echarts — IgnorePlugin
The difficulty in optimizing Echarts is that there are two approaches used at the beginning of the project:
- I introduced the third party Vue-Echarts and wrote my own public component base-chart based on this component, but didn’t use this library. Some of the page charts are based on base-Chart and the relevant components such as Echarts /lib/line are introduced as needed.
- There are components that refer directly to native Echart that write from 0.
This leads to the problem of echarts being introduced in full and packaged everywhere.
Since our home page is the login page and we don’t use echarts, we don’t need to load echarts the first time, so we need to do two things to optimize:
- This third party Vue-echarts is registered globally in main.ts, but is not actually used, so remove the global reference to avoid packaging it into chunk-vendors.
- Detach the repackaged parts and merge them into a chunk.
// Main. ts component registration code should also be commented out
// import ECharts from 'vue-echarts';
// Vue.component('echart', ECharts);
optimization: {
splitChunks: {
cacheGroups: {
echarts: {
name: 'echarts'.test: /[\\/]node_modules[\\/]echarts[\\/]/.minSize: 0.minChunks: 1.reuseExistingChunk: true.chunks: 'all',},},},},Copy the code
It has been optimized to pull out of the chunk-vendor and merge multiple existing Echarts references into a bundle. But you can see it’s still pretty big.
There was an issue on Github about packaging being too bulky, and the author suggested using an online Builder to pack as needed depending on the project. It said that version 5.0 might consider reducing the packaging size.
However, in the actual use process, some resources 504 gateway timed out. After several retries, it failed, so we had to find another method.
Use the IgnorePlugin plug-in built into WebPack to ignore files that are not used in your project. Can compare the website of online Builder. Component, chart, and Coord in node_modules/echarts/lib are excluded.
In the IgnorePlugin plug-in configuration item, you need to first use the contextRegExp to determine the context of the file to be excluded, in this case the echarts directory. Then use resourceRegExp to specify a regular expression for the resources to exclude. In fact, only these directories are excluded, and there are other files at the same level that may be relevant to the diagram/component to be excluded, but in order to avoid misjudgments, this can’t be done in that detail.
plugins: [
new webpack.IgnorePlugin({
resourceRegExp:
/^\.\/lib\/(component\/visualMap|toolbox|timeline|geo|brush|calendar)|(chart\/effectScatter|candlestick|heatmap|tree|tre emap|sunburst|map|graph|boxplot|parallel|gauge|funnel|sankey|themeRiver|pictorialBar)|(coord\/polar|geo|singleAxis|calen dar)$/.contextRegExp: /echarts$/})]Copy the code
After optimization, it immediately becomes much smaller:
Optimization of ant – design – vue
Before optimization:
Discovering ICONS takes up a lot of space, but when we actually use them we rarely use them. Someone mentioned the issue on GitHub and the author explained that buttons automatically refer to ICONS. That’s the design. The ant-design has been optimized. For now, the method recommended by the author is used to introduce ICONS on demand: add an alias, let webPack parse using the path in the icons.js file provided by us, and only package the used ICONS.
resolve: {
alias: {
'@ant-design/icons/lib/dist$': resolve('./src/core/antd/icons.js')}},Copy the code
Then add the corresponding file to the SRC directory, as shown on Github
export {
default as SettingOutline
} from '@ant-design/icons/lib/outline/SettingOutline'
export {
default as GithubOutline
} from '@ant-design/icons/lib/outline/GithubOutline'
export {
default as CopyrightOutline
} from '@ant-design/icons/lib/outline/CopyrightOutline'
/* MultiTab begin */
export {
default as CloseOutline
} from '@ant-design/icons/lib/outline/CloseOutline'
export {
default as ReloadOutline
} from '@ant-design/icons/lib/outline/ReloadOutline'
export {
default as DownOutline
} from '@ant-design/icons/lib/outline/DownOutline'
export {
default as AlignLeftOutline
} from '@ant-design/icons/lib/outline/AlignLeftOutline'
/* MultiTab end */
/* Layout begin */
export {
default as LeftOutline
} from '@ant-design/icons/lib/outline/LeftOutline'
export {
default as RightOutline
} from '@ant-design/icons/lib/outline/RightOutline'
export {
default as MenuFoldOutline
} from '@ant-design/icons/lib/outline/MenuFoldOutline'
export {
default as MenuUnfoldOutline
} from '@ant-design/icons/lib/outline/MenuUnfoldOutline'
export {
default as DashboardOutline
} from '@ant-design/icons/lib/outline/DashboardOutline'
export {
default as VideoCameraOutline
} from '@ant-design/icons/lib/outline/VideoCameraOutline'
export {
default as LoadingOutline
} from '@ant-design/icons/lib/outline/LoadingOutline'
export {
default as GlobalOutline
} from '@ant-design/icons/lib/outline/GlobalOutline'
export {
default as UserOutline
} from '@ant-design/icons/lib/outline/UserOutline'
export {
default as LogoutOutline
} from '@ant-design/icons/lib/outline/LogoutOutline'
/* Layout end */
Copy the code
The optimization has been much more low-key:
So far, the project packaging has been optimized to a large extent. Compared with before optimization, the total volume of the packaging has been reduced by about 1/3, and the volume has been reduced by about half after compression, and finally reduced to KB level. Congratulations!
Before optimization:
- Stat: 10.54 MB
- Parsed: 4.94 MB
- Gzipped: 1.55 MB
After the optimization:
- Stat: 7.57 MB
- Parsed: 3.2 MB
- Gzipped: 1003.36 KB
conclusion
- For modules that are not needed on the home page, try not to use synchronous references (import XX from ‘… ‘) in the entry file to avoid packaging together to reduce the first request time and speed up the rendering of the home page;
- When using third-party libraries, try to import them as needed. If needed, use IgnorePlugin or ContextReplacementPlugin to tell WebPack which files we do/do not need to package;
- To extract the common module using splitChunks, note the value of the chunks attribute if it is introduced on demand (
import()
) using synchronously imported modules, chunks is set toinitial
It’s useless. This is why the vue-CLI default splitChunks don’t help us pull out some duplicate code, it only helps us with synchronized modules:
// ...
optimization: {
minimizer: [
// ...].splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors'.test: /[\\/]node_modules[\\/]/.priority: - 10.chunks: 'initial'
},
common: {
name: 'chunk-common'.minChunks: 1.priority: - 20.chunks: 'initial'.reuseExistingChunk: true}}}}Copy the code
Optimize packing speed
Use the speed-measurement-webpack-plugin to measure the time spent in each step of packaging
Vue – Cli:
// yarn add speed-measure-webpack-plugin --dev
// vue.config.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
module.exports = {
// There is no way to chainWebpack
configureWebpack: smp.wrap({
// ... webpack config goes here ...}}Copy the code
Run the yarn build command
Use DLLS to extract common libraries that are updated infrequently
Update in the…