Webpack打包优化实战:从体积瘦身到构建加速
本文深入解析Webpack打包优化的核心技巧,涵盖从定位大体积模块、提取公共代码、移除无用资源到模块化引入及CDN外部引用的全流程。重点讲解CommonsChunkPlugin与DLLPlugin的配置细节,旨在帮助开发者显著减小vendor.js体积并大幅提升构建效率,改善前端项目加载体验。
Webpack打包优化概述
前端构建工具经历了从Browserify、Grunt、Gulp到Rollup、Webpack的演进,涌现出众多优秀方案。目前,Webpack无疑是使用最广泛的打包工具,无论是主流前端框架还是各类工具库,大多首选其作为构建基础。因此,在开发中选用Webpack作为打包工具是一个明智的选择。在最近的项目实践中,我也应用了Webpack,并遇到了一些关于优化的问题,在此总结Webpack打包优化的相关细节与方法。
本次项目使用了Vue全家桶,Webpack配置直接采用vue-cli生成的默认设置。项目打包后发现生成的vendor.js文件体积异常庞大,且打包过程耗时极长。为此,我尝试了多种优化手段以改善这一状况。
定位体积大的模块
定位体积大的模块
要优化打包体积,首要任务是识别出占用空间较大的模块。我们可以借助webpack插件webpack-bundle-analyzer来查看整个项目的体积结构对比。该插件以Treemap(树状地图)的形式直观展示结果,并支持具体的交互操作。它不仅能列出项目中引用的所有依赖,还能清晰显示各个模块体积在整个项目中的占比。

该插件的安装方法可通过 npm install webpack-bundle-analyzer --save-dev 执行,并在webpack配置信息的 plugins: [new BundleAnalyzerPlugin()] 部分添加相应配置即可。对于vue-cli的配置方式,默认已安装该插件但未启用。找到config/index.js文件,在build配置下会有bundleAnalyzerReport的配置项,默认值为process.env.npm_config_report 。建议在package.json的scripts中添加一行 "analyz": "npm_config_report=true npm run build" ,这样每次需要启用该插件时,只需执行 npm run analyze 命令即可。
提取公共模块
提取公共模块
Webpack在模块化打包方面有两个核心功能:一是支持大量模块类型,无论是TypeScript、CoffeeScript还是Sass、Stylus等语言均能处理;二是可以通过配置控制打包文件的粒度,这一点将在下文详细阐述。
在开发中,我们通常会将所有依赖库单独提取出来,不与项目代码混合。这时会用到Webpack自带的插件CommonsChunkPlugin ,从名称即可看出它是一个用于提取公共模块的插件。根据文档说明,可以传入一个对象作为参数,常用参数包括:
- name(names)
- minChunks
- chunks
name参数易于理解,指最终打包文件的名称;若使用names参数,则必须传入字符串数组。minChunks参数若为数字,表示当模块被其他模块引用的次数达到该数值时,该模块将被打包;若为函数,则返回值必须为布尔类型,用于指示该模块是否应被打包进公共模块。chunks参数指定一个字符串数组,设置后打包时仅从指定的模块中提取公共子模块。
下面通过几个实例说明该插件的工作原理。
假设有两个模块chunk1.js和chunk2.js,以及两个项目文件a.js和b.js,其结构大致如下:
// a.js
require('./chunk1');
require('./chunk2');
require('jquery');
// b.js
require('./chunk1');
require('./chunk2');
require('vue');
// webpack配置如下
module.exports = {
entry: {
main: './main.js',
main1: './main1.js',
jquery:["jquery"],
vue:["vue"]
},
output: {
path: __dirname + '/dist',
filename: '[name].js'
},
plugins: [
new CommonsChunkPlugin({
name: ["common","jquery","vue","load"],
minChunks:2
})
] };
最终的打包结果为:jquery被打包到jquery.js,vue被打包到vue.js,common.js打包的是公共模块(chunk1和chunk2)。使用该插件打包时,满足minChunks条件的模块会被打包到name数组的第一个块中,随后数组后面的块依次打包。首先从entry中查找,若未找到则生成一个空块。name数组中的最后一个块打包的是Webpack的runtime代码,使用时必须先加载该块。
现在来看vue-cli对该插件的配置文件:
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
// 将node_modules中的依赖模块全部提取到vendor文件中
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// webpack在使用CommonsChunkPlugin时会生成一段runtime代码,并且打包进vendor中。
// 这样即使不改变vendor代码,每次打包时runtime会变化导致vendor的hash变化,这里
// 把独立的runtime代码抽离出来来解决这个问题
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
}),
移除不必要的文件
移除不必要的文件
在项目中,通过方法一我定位到几个体积占用较大的库,其中之一便是moment.js日期处理库。对于日期处理功能,该库为何占用如此大的体积?仔细查看发现,引用该库时引入了所有locale文件,而这些文件占据了库体积的大部分。因此,在Webpack打包时移除这部分内容可以有效减小打包文件的体积。
Webpack自带的两个库可以实现这一功能:
- IgnorePlugin
- ContextReplacementPlugin
IgnorePlugin 的使用方法如下:
// 插件配置
plugins: [
// 忽略moment.js中所有的locale文件
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
// 使用方式
const moment = require('moment');
// 引入zh-cn locale文件
require('moment/locale/zh-cn');
moment.locale('zh-cn');
ContextReplacementPlugin 的使用方法如下:
// 插件配置
plugins: [
// 只加载locale zh-cn文件
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/),
],
// 使用方式
const moment = require('moment');
moment.locale('zh-cn');
通过上述两种方式,moment.js的体积大致能缩减为原来的四分之一。
模块化引入与CDN引用
模块化引入
在项目中我使用了lodash这一常用工具库,但在代码定位时发现该库也占用了不少体积。仔细思考,使用这类工具库时往往只用到其中很少的一部分功能,却引入了整个库。因此,这里可以进一步优化,仅引用需要的部分。
import {chain, cloneDeep} from 'lodash';
// 可以改写为
import chain from 'lodash/chain';
import cloneDeep from 'lodash/cloneDeep';
这样就可以只打包我们需要的部分功能。
通过CDN引用
对于一些必要的库,若无法对该库进行更好的体积优化,可以尝试通过外部引入的方式来减小打包文件的体积。采用该方法只需在CDN站点找到需要引用的库的外部链接,并对Webpack进行简单配置即可。
// 在html中添加script引用
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
// 这里externals的key指的是使用时需要require的包名,value指的是该库通过script引入后在全局注册的变量名
externals: {
jquery: 'jQuery'
}
// 使用方法
require('jquery')
DLLPlugin与DLLReferencePlugin拆分文件
通过DLLPlugin和DLLReferencePlugin拆分文件
如果项目规模过大,打包时间会非常长。若频繁更新上线,则需不断对代码进行编译打包,浪费大量时间。此时,我们可以将那些不常更新的框架和库(如vue.js等)进行单独编译打包。这样,每次开发上线只需对开发文件进行编译打包,从而极大节省不必要的打包时间。这种方法需要DLLPlugin 和DLLReferencePlugin 两个插件配合完成。
DLLPlugin
在使用该插件时,我们需要单独创建一个配置文件,这里命名为webpack.dll.config.js,配置如下:
module.exports = {
entry: {
lib: ['vue', 'vuex', 'vue-resource', 'vue-router']
},
output: {
path: path.resolve(__dirname, '../dist', 'dll'),
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath,
library: '[name]_library'
},
plugins: [
new webpack.DefinePlugin({
'process.env': '"production"'
}),
/**
* path: manifest.json输出文件路径
* name: dll对象名,跟output.library保持一致
*/
new webpack.DllPlugin({
context: __dirname,
path: path.resolve(__dirname, '../dist/dll', 'lib.manifest.json'),
name: '[name]_library'
})
]
}
这里要注意几点:
- entry中写明所有要单独打包的模块
- output的library属性可以将dll包暴露出来
- DLLPlugin的配置中,path指明manifest.json文件的生成路径,name暴露出dll的函数名
运行该配置文件便可生成打包文件和manifest.json文件。
DLLReferencePlugin
对于该插件的配置,不需要像上面一样单独写配置文件,只需要在生产配置文件中添加如下代码:
new webpack.DllReferencePlugin({
context: __dirname, // 同dll配置的路径保持一致
manifest: require('../dist/dll/lib.manifest.json') // manifest的位置
}),
然后运行Webpack,发现打包速度得到了极大提升,且无需每次更新代码时重复编译打包这些依赖库。
其他优化手段
其他
关于Webpack的打包优化,我大致总结了上述方法。为了让页面加载更快,提供更好的用户体验,我们不仅要从打包上优化,还可以从其他方面入手。这里我也简单提一下我使用过的方法。
开启Gzip压缩
开启Gzip压缩可以减少HTTP传输的数据量和时间,从而减少客户端请求的响应时间。由于降低了请求时间,页面加载速度也会得到提升,渲染速度更快,极大地改善了用户体验。由于目前几乎所有主流浏览器都支持Gzip压缩方式,只需对服务器进行相关设置即可,这里就不具体讲解如何配置服务器。
压缩混淆代码
我们平常也会对代码进行压缩混淆,可以通过UglifyJS 等工具对JS代码进行压缩。同时,去掉不必要的空格、注释、console信息等,也能有效减小代码体积。
总结
总结
本文到此结束,主要对Webpack的打包优化部分做了讲解。当然,受限于能力和时间,只研究了部分方法,可能还有其他更多的优化手段,无论是从编译打包的体积还是速度上,都能有更好的优化。接触了一段时间的Webpack后发现,作为一个打包工具,它确实过于复杂,无论从最初的官方文档还是到新的高级特性,都很难完全掌握,仍需自己不断实践和研究。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
UmiJS 项目构建速度优化与打包体积精简技巧
2026-11-04 22:37:09
原生JS操作DOM与Vue/React虚拟DOM的本质区别
2026-11-04 17:52:27
怎么使用console.group和table优化JS日志可读性
2026-11-03 23:14:34
基于AST抽象语法树的JS代码反混淆实战教程
2026-11-03 21:22:24
JS手机号输入校验的性能优化与用户体验提升
2026-11-03 19:19:19








