开始接触 vue 的时候大概就是开始接触 webpack 的时候,用 vue-cli 直接脚手架工程后就开始开发了,用了一段时间后发现 vendor.js 越来越大,居然到了 M 的级别。看了一下 vue-cli 的默认配置,vendor 是把 node_modules 里的依赖都打进 vendor 中,我觉得太大了这样,不如手动维护控制包的粒度,多分出几个包,充分利用浏览器并发请求资源的好处,而不是把所有东西都打进 vendor 中。
那个阶段开发任务重,没太多时间细细纠结,反正是迷迷糊糊看着 webpack 的配置文档和各种问题解决的帖子把上面提到的事儿做了。效果出来了,也就没再管过。这几天突然发现打包的地方还是有很多可以优化的点儿,便优化并记录一下。
这里着重的就是三个东西来完成此次优化:
首先,记录一下目前线上的包有什么问题(这个项目是一个多入口的 vue 项目):
所以接下来我们就利用工具解决上面的问题到达优化的目的。
CommonsChunkPlugin 就是常用的提取各个包共有模块的工具,不过我觉得这个插件设计的不是那么易于理解和使用,文档看了有些懵。。。不过我还是按照自己的实验和理解来记录一下如何使用他。
在分析之前,我是一脸懵逼的,我只知道它打出的包不是我想要的,而不知道打出包的内容到底哪里不对,对整个出包的结果不清晰和不确定让我很抓狂,我不知道眼前的包是什么细节的,怎么优化呢?没错,无意间找到了默认就有的一个神器,只要开启一下 BundleAnalyzerPlugin 就可以图形化的观察打包的结果, 先上一张优化前的图:
我们可以看出各个包的组成,里面明显的问题:
好的,目标确定了,然我们来改我们的配置,这里记录关键配置:
- entry: {
- // ... 这里的5入口模块文件就省略了
- vendor: ['vue', 'vue-router', 'vuex', 'element-ui'],
- echarts: ['vue-echarts'],
- lodash: ['lodash']
- }
可以看到在入口处,除了我们的入口文件声明,我们还声明了非入口包文件:
以上手动维护自己的包文件构成,我觉得比 vue-cli 默认那种把 node_modules 都打进 vendor 来要好,因为我们可以更好的控制单个包文件的大小和粒度。
然后我们就需要配置 CommonsChunkPlugin 这个插件去完成公共包的抽离,并且命名为 common.js,(如果你还记得一切都是模块,那么我们还会顺带完成我们另一个任务,即 common.css), 配置如下:
- plugins: [new webpack.DefinePlugin({
- 'process.env': env,
- }), new webpack.optimize.UglifyJsPlugin({
- compress: {
- warnings: false
- }
- }), new OptimizeCssAssetsPlugin({
- assetNameRegExp: /\.css$/,
- cssProcessorOptions: {
- discardComments: {
- removeAll: true
- }
- }
- }),
- // extract css into its own file
- new ExtractTextPlugin({
- filename: utils.assetsPath('css/[name].[contenthash].css')
- }),
- // 抽取公共模块
- new webpack.optimize.CommonsChunkPlugin({
- names: ['common', 'lodash', 'echarts', 'vendor']
- }),
- // 抽取 manifest
- new webpack.optimize.CommonsChunkPlugin({
- name: 'manifest',
- minChunks: Infinity
- })]
这个 CommonsChunkPlugin 不好理解的在于,它是一个隐式的配置,即 names: ['common', 'lodash', 'echarts', 'vendor'] 这个配置中数组中的模块顺序是有讲究的。。。可以看看这个 issue: https://github.com/webpack/webpack/issues/1943 简单来说就是它们的依赖关系如下:
- a ->
- b -> common -> lodash -> echarts -> vendor -> manifest
- c ->
就是入口文件 a,b,c 依赖 common.js, common.js 依赖 lodash, 依次类推,但其实也许 common.js 不依赖 lodash,但是由于数组是平的结构,不能表示树形结构,所以这么写没有问题,就是被依赖的包不提取依赖的模块到自己的包中罢了。。。好绕嘴。。。,我们肯定的是入口 abc 一定依赖他们后面的东西(CommonsChunkPlugin 不配 chunks 属性,默认是所有模块,所以 common 的声明会使得入口的公共代码被打进去,而像 lodash echarts vendor 这种我们声明的模块如果被 common 吸收了也会挪出去的,其实就是这个依赖链条你必须告诉 webpack,这也就是说你只声明一个 names: ['common'] 会打重复的包,因为 webpack 并不知道 common 和定义的包之间什么关系),所以记住数组中是:子模块 -> 父模块 这么一个顺序写的,子依赖父,而当我们用 htmlWebpackPlugin 自动写入链接的时候,顺序刚好和声明相反,即:
- <script type=text/javascript src=manifest.js >
- <script type=text/javascript src=vendor.js >
- <script type=text/javascript src=echarts.js >
- <script type=text/javascript src=lodash.js >
- <script type=text/javascript src=common.js >
- <script type=text/javascript src=a.js >
这样就到达了我们的目的,至于 manifest 是什么,这又是一个问题,可以参考 webpack 官网或者 https://github.com/webpack/webpack/issues/1315
这样我们同样提出了我们的 common.css,然后利用 OptimizeCssAssetsPlugin 做了压缩。好了,然后让我们再打包一遍,看看是什么样子了:
恩看起来一目了然,是我们想要的结果,由于观察不出 css,和想要实地看看我们的收益,做了一下优化前后的网络性能截图。
可以明显看出:
最后顺手记录了下白屏的影响:
效果还是明显的,用户感知度更好,整体感知时间缩短了一半,从 1.14s 降低到了 597ms,从秒级别降低到了毫秒级别。
ps:嗯嗯,最近发生了很多事儿,可是呢加油吧 ~ @simona 祝顺利 ~
来源: http://www.tuicool.com/articles/vEFre2R