Optimizing Vue/Vuetify Projects with Webpack: Code Splitting, CommonsChunkPlugin, and Externals
By using Webpack’s CommonsChunkPlugin to extract node_modules into a vendor chunk, configuring externals to load Vue and Vuetify from a CDN, and converting non‑critical components to async imports, a Vuetify‑based app reduces its first‑screen bundle by hundreds of kilobytes, improves caching, and speeds initial load.
In the HTTP/1 era, a common performance optimization was to merge JavaScript files to reduce request count. However, bundling a very large JS file can be counter‑productive. By splitting code into first‑screen and non‑first‑screen parts, and separating business code from library code, browsers can better leverage caching and improve initial load speed.
Core Idea 1 – Separate Business Code and Base Libraries Business code changes frequently, while base libraries (e.g., Vue, Vuetify) change rarely. Splitting them allows the browser to cache the libraries for a longer period.
Core Idea 2 – Load on Demand (Async) Only the code required for the first screen should be loaded initially; other routes can be loaded asynchronously.
Practical Example A Vuetify‑based internal system was initially built with a default Webpack configuration, resulting in several large bundles. Using webpack-bundle-analyzer revealed that Vue, Vuetify and other modules were duplicated across bundles.
First, the CommonsChunkPlugin was used to extract common vendor code:
entry: {
vendor: ['vue', 'vue-router', 'vuetify', 'axios'],
app: './src/main.js'
},However, manually listing vendor modules is not scalable. By using minChunks with a function that checks the module’s path, all modules under node_modules are automatically extracted:
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: ({ resource }) => (
resource &&
resource.indexOf('node_modules') >= 0 &&
resource.match(/\.js$/)
)
}),After this step, the bundle analysis showed that all node_modules code was grouped into the vendor chunk.
Next, the async option of CommonsChunkPlugin was used to create a shared chunk for modules that are used in at least two async chunks (e.g., codemirror , custom components):
new webpack.optimize.CommonsChunkPlugin({
async: 'used-twice',
minChunks: (module, count) => (count >= 2)
}),This moved frequently reused components into a separate used-twice‑app.js file, reducing the size of each page by roughly 10 KB.
To avoid bundling the bulky Vuetify library, externals were added and the library was loaded via CDN in index.html :
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" rel="stylesheet" type="text/css">
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.js"></script>The corresponding Webpack externals configuration:
externals: {
'vue': 'Vue',
'vuetify': 'Vuetify'
},After applying externals, the Vue‑related code disappeared from the bundle, shrinking app.js by nearly 200 KB.
Finally, components that were still duplicated (e.g., codemirror ) were converted to async imports:
// const MCode = "../component/MCode.vue"; // comment out
components: {
MDialog,
MCode: () => import(/* webpackChunkName: "MCode" */ '../component/MCode.vue')
},This further reduced the first‑screen bundle by about 150 KB.
Complete Optimized Webpack Configuration (excerpt)
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const generateHtml = new HtmlWebpackPlugin({
title: '逍遥系统',
template: './src/index.html',
minify: { removeComments: true }
});
module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].[hash].js',
chunkFilename: '[id].[name].[chunkhash].js'
},
resolve: {
extensions: ['.js', '.vue'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'public': path.resolve(__dirname, './public')
}
},
externals: {
'vue': 'Vue',
'vuetify': 'Vuetify'
},
module: {
rules: [
{ test: /\.vue$/, loader: 'vue-loader', options: { loaders: {} } },
{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
{ test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { objectAssign: 'Object.assign' } },
{ test: /\.css$/, loader: ['style-loader', 'css-loader'] },
{ test: /\.styl$/, loader: ['style-loader', 'css-loader', 'stylus-loader'] }
]
},
devServer: { historyApiFallback: true, noInfo: true },
performance: { hints: false },
devtool: '#eval-source-map',
plugins: [
new CleanWebpackPlugin(['dist']),
generateHtml,
new BundleAnalyzerPlugin(),
new webpack.optimize.CommonsChunkPlugin({ name: 'ventor', minChunks: ({ resource }) => (resource && resource.indexOf('node_modules') >= 0 && resource.match(/\.js$/)) }),
new webpack.optimize.CommonsChunkPlugin({ async: 'used-twice', minChunks: (module, count) => (count >= 2) }),
new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }),
new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }),
new webpack.LoaderOptionsPlugin({ minimize: true })
]
};
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map';
module.exports.plugins = (module.exports.plugins || []).concat([
new BundleAnalyzerPlugin(),
// other production plugins
]);
}Summary of the Optimization Steps
Use CommonsChunkPlugin with minChunks to separate base libraries (node_modules) from business code and create a shared chunk for modules reused multiple times.
Leverage Webpack externals to exclude Vue and Vuetify from the bundle and load them via CDN.
Apply async component loading (dynamic import() ) to split non‑first‑screen code.
After applying all these techniques, most business‑related JS files were reduced to under 50 KB (excluding source maps), achieving a successful performance optimization.
References
Webpack 大法之 Code Splitting: https://zhuanlan.zhihu.com/p/26710831
vue+webpack实现异步组件加载: http://blog.csdn.net/weixin_36094484/article/details/74555017
VUE2组件懒加载浅析: https://www.cnblogs.com/zhanyishu/p/6587571.html
Tencent Music Tech Team
Public account of Tencent Music's development team, focusing on technology sharing and communication.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.