注册
web

因为打包太慢,我没吃上午饭

事情的起因是这样的:


鄙人呢,在公司负责一个小小的后台管理系统。


这天中午临近饭点的时候,测试小哥坐我工位旁边现场监督我改一个Bug。


Bug本身它倒是不复杂,甚至时隔几天之后我已不记清具体内容是什么。当时只见我是三下C两下V、提交、合并测试分支、登录Jekins点击deploy,一顿操作如行云流水一般丝滑。


wyzjy.gif


说时迟,那时快。公司给 极品廉价劳动力们 我们安排的午饭送到了,一人一份。但你也知道,这两年经济下行,无人幸免;有的人食不果腹,有的人衣不蔽体, 保不齐谁饿的紧,拿了两份饭,那可就意味着有一个人要饿肚子,我可万万不希望那个倒霉蛋是我。


看着Jekins的deploy进度条,我对测试小哥说:


“你先回去,等会发完了你再看一下,应该没有问题,我先去干饭了”


说罢,我转头看向测试小哥:他面无表情盯着屏幕,似乎是默许了我的提议。于是我便准备起身——


只见他头也不回一手把我按住,缓缓吐出四个字:


“看完再吃”


...


...


...


大约半个小时后,KFC。


我:“我都告诉你了,不会有问题,先干饭,你非不听”


测试小哥:“......”


我:“这下好了吧,上个月的工资还没发,现在又来付费上班”


测试小哥:“我就问你,星期四的这个辣翅,它香不香”


我:“香”


image.png


罪魁祸首


所以,项目到底deploy了多久?


f7c9f47e6cbb14b80745a39c3946832.png


Jekins的记录中可以看到,编译打包环节耗时基本在五六分钟,最近几次成功构建的整体耗时平均在5分50秒。


这个项目本身呢,说大不大,说小也不算小。是个普通CRUD页面居多的管理后台,没有太多其他乱七八糟的东西。如果以页面、组件数量的维度来看:


使用资源管理器在项目的/src目录下通配*.vue可以看到有561个文件


image.png

image.png


说实话,这样的体量打包5-6分钟,属实有点过分。


我又找来公司的一个巨石应用来对比:由于巨石应用历史比较悠久,横跨了多个技术栈(HandleBars模板引擎、使用jQuery的原生HTML、Vue2),不能只看SFC的数量,所以这次就来比较一下,用来存放页面文件的文件夹的体积


image.png


我的项目








image.png


巨石应用


先不算其他的资源,单就页面文件体积已经接近5倍,如果其他东西都算上,打包时间就算没有5倍,一两倍总是要有的吧?


结果呢,时间甚至更短


image.png

好好好,有活干了。为了避免下次面试官问我对webpack做过什么优化时复述那些网上千篇一律的答案,现在就来实际操作一下。



本文真实记录了一次对项目构建耗时、产物体积优化的过程。没有对知识点系统的梳理,主要突出的是思路:面对问题时的解决思路。



日志分析


曾经有位技术能力超强的架构师说过:遇到问题不要慌,先看日志。


既然这么慢的构建过程是发生在Jekins上,那就先来捋一捋Jekins的log,有没有什么值得注意的地方。


这个项目的构建脚本中,抛开(Jekins)工具的准备、从git上拉取代码以及最后的部署这些动作,只看跟前端的打包有关的部分,命令很简单,只有四行:


rm -rf node_modules
rm package-lock.json
npm i
npm run build

在日志中体现如下图:


0c4ebfe0835a4da8d477713af40285d.png


35c974d100d807a636a530608ae602f.png


开局就是一记暴击!


v7m5.gif


11:22:08 + npm i
11:25:59 + added 1863 packages from 1199 contributors balabala...
...
...
11:25:59 + npm run build
...
...

合着这五六分钟的打包时间,安装依赖就占了一大半,阿西巴!


在继续往下进行之前,请允许我先介绍些项目的其他背景:



deploy脚本拉取代码这一步简单来说就是:cd进项目目录 -> git pull -> 切换至要构建的分支


项目的开发人员较少,算我在内三个人


项目的依赖变动频率十分低,以月或数月为单位



背景铺垫完了,开始研究npm i为什么这么耗时,相关的命令有三句:


rm -rf node_modules
rm package-lock.json
npm i

其中npm i这句是必须的,没什么好说的;rm -rf node_modulesrm package-lock.json这两句是变量,挨个做耗时的对比测试。


首先,我在本地使用跟Jekins上相同的node版本(14.16.1),使用相同的npm源(官方源),新建一个目录clone项目代码:



  1. 完整执行三行命令,耗时与Jekins上相差无几
  2. (此时已经有了package-lock.json文件)执行rm -rf node_modules + npm i,耗时极短
  3. (此时已经有了node_modules目录)执行rm package-lock.json + npm i,耗时也极短

第三步其实没什么意义,npm文档中有提到,这种情况就相当于梳理了node_modules的结构并生成了package-lock.json,并没有安装任何东西。
image.png


而步骤一和步骤二之间为何耗时相差比较大,可以参考这篇我觉得npm install流程写的比较好的文章:简单来说就是花费了大量时间去远端获取包信息


结合项目背景一,我们的package-lock.json会提交到仓库,每次肯定都是最新的,所以Jekins deploy脚本中rm package-lock.json这一步属实是没有必要,本地计算完了到Jekins上又来一遍,纯纯的浪费时间。


联系运维哥把测试环境的deploy脚本修改一下,去掉了rm package-lock.json这一句,测试下耗时:


c7085e76a3aed2877d8b6fdfb846fb9.png

如图中下边两次构建,【编译打包】环节时间都去到了2分30秒左右,直接缩短了一半多。部署成功后,在测试环境的页面咔咔一顿点,似乎也没有什么依赖包引起的报错。


效果是不是很显著,你以为这就完了?不不不,图中那条49秒的构建我可不是大意截进去的,伪装成不经意的失误,就是为了丝滑的承上启下


QwN4E.gif


既然这个项目的开发人员很少,而且依赖的添加/更新频率又极低,也就意味着每次npm i所安装的东西,基本都是一样的,既然都一样,为什么我还要rm -rf node_modules再安装?


想象一下你日常本地开发时,如果某次需求要用到一个新的npm包,你一定是先npm install xxx,除非碰到了依赖冲突,否则不会清除node_moduels重新安装。


明明npm提供了梳理依赖树、只做局部更新的逻辑,我们却偏偏每次清除node_modulesinstall,这种行为吧,我感觉就像明明是个Vue项目,却在里边到处使用Document API


哎嘿,我就不用你的响应式,就是玩~


lKGp.gif


冒着被打的风险又私聊了运维哥,把rm -rf node_modules去掉,再发布了一次看看效果


677c1174550bece7d6fabb58a09597a.png

优秀!打包时间从5分多直接干到了50秒,优化率80%+!


本文结束!



在正式结束前,觉得还是有必要补充两点



  1. 各位读者在做打包优化时,部署脚本是否清除package-lock.jsonnode_modules还是主要取决于项目实际情况和团队协作模式,不能因为一味的追求构建速度而导致频繁的构建失败/安装依赖失败[滑稽]
  2. 如果您经过深思熟虑后觉得还是有必要清除package-lock.jsonnode_modules,山人还有一计可供大王优化构建速度:打包时离不开babel,但babel又是个老大难,好在它能缓存转换结果。一般情况下缓存会放在项目目录下的/node_modules/.cache/,那我们把删除node_modules的命令稍微改那么亿点:

    find node_modules/ -mindepth 1 -maxdepth 1 ! -name '.cache' -exec rm -rf {} +


    删除node_modules里面除了/.cache目录以外的其他内容,这样在构建过程中babel还是能使用到之前的缓存。那速度,体验过的都说好!(看babel-loader的缓存文件有多大)


    0716d75b5c769cef10689560947061b.png










全面升级


如果是本着以后不影响吃午饭的目的,那现在确实可以结束了。但我自幼便深受中国四大名句之一来都来了的兄弟句式——干都干了的文化熏陶:既然已经开始了,那就干脆给项目做个全套大保健!


image.png


不过此时我和在座的各位都一样,对打包优化这块着实没什么经验,可以说是毫无头绪。


浅浅百度了下webpack打包优化,有两个工具基本每篇文章都有提到:打包耗时分析speed-measure-webpack-plugin、打包体积分析webpack-bundle-analyzer(vue-cli内置)


目前的痛点是,那就先来个耗时分析试试水。


使用方法还是老样子,自己去查,别人都写的我就不再重复写了


效果如图:


2d1e9f581d75198215a76ae86e410db.png


9f5463a422434e783d851fe6cf13dbc.png


此时因为babeleslint还没有缓存,耗时多是意料之中的;其他的loader或多或少的三两组合,展示了一个module计数和耗时小计,我从中并没有办法获得什么有用的信息;并且多次构建对比发现loader组合的规律和耗时的排名也无迹可寻。在我的认知里:所有被命中的文件会按照loader配置的顺序依次处理,所以面对这样的结果我实在是无计可施。(有会看的朋友可以补充一下)


翻看speed-measure-webpack-plugin的文档,发现有可以打印耗时top N文件的配置项,但开启后再次构建得到的这些文件,同样令我摸不着头脑:一个寥寥数十行的SFC小组件,css-loader耗时竟然能用四到五秒!要知道里边只有一条scoped的样式规则。


无奈只好放弃,看了下项目用的是vue-cli@4.x创建的,对应的webpack@4.x,那就去webpack的文档里逛逛碰碰运气吧!


可惜,福无双至祸不单行。文档里翻了半天,耳熟能详、配置简单的路子,例如babel-loader、eslint-loader的编译缓存多线程打包chunk分割代码混淆压缩tree shaking这些,要么是之前已经被配置过了,要么是webpack内置了。而复杂、高级一些的优化方式,我的项目又用不到...


直到我看到了这里:


image.png

升级webpack简单(呸),npm upgrade webpack嘛,先来搞这个~~


回到项目的package.json里,咦,好奇怪,没有webpack,也没有vue-cli


vue-cli是装在全局的,而webpack是作为依赖的依赖安装的,没有体现在package.json中,所以直接npm upgrade webpack应该是不行的。vue-cli文档提供了一个升级的命令:vue upgrade


既然要升级,干脆全上新的!Node也给他干到20!(我也不知道我当时为什么要这么做,但这为后来的事情埋下了伏笔。。。)


vue-cli升级完,扫了一遍webpack升级指南,发现我项目里的配置文件也没什么好改的,Nice!


image.png


本地浅浅的run了一下server、run了一下build,发现也都OK!那就提交上去在Jekins上试试Node V14o不ok


emmmm...


报错倒是没报错,只是...


4f7cb2e83ffe8afb4db6cd7c5b02221.png

本地build的时候没注意,Jekins上跑才发现,怎么慢了这么多!说好的更新到最新版本均有助于提高性能呢?


再看看这构建物的体积


05d17813e68c984f311cff8868385fb.png


image.png


Hà的我赶紧又本地build了一次,还真让我发现了些东西:似乎build了两次


4e9531cc221e18e94592ac4df18ff6f.png


image.png


按理来说应该只有下边这个print,那上边的legacy bundle又是什么东西?百度上随便那么一搜,应该是不少人都被这么坑过,很容易搜到:这是一种兼容性的构建产物,主要是为了兼容一些很古老版本的浏览器/客户端。想控制也很容易,改package.json里的browserslist字段即可。


这就好办了,这项目是我们公司的内部项目,考虑兼容性?不存在的。


{
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}

配置了之后又试了下,基本恢复到了升级webpack之前的水平,但还是慢一点点...


构建速度的优化这块,实在是没头绪了,明明升级了webpack版本,构建速度却变慢了。


不过刚才提到的两个工具,构建耗时分析的用过了,还有个vue-cli内置的构建体积分析工具没体验;如果需要打包的东西变少了,那构建速度应该也能快一点吧!(吧?)


塑形瘦身


在正式瘦身前,有一个小插曲:


不知道在座的各位,项目里有没有这样的东西


console.log(123123)
// or
console.log('asdfasdf')

我是一个崇尚极简的人,我能接受的底线也就是


console.log('list data: ', data);

仅此而已


你要打印接口返回数据,Network里能看


你要打印函数中某个变量的值,可以打断点


我实在是想不出什么必须console.log的场景


如果你说为了方便线上调试


我能接受的最多也就是按规范打印有意义的log


更别提项目首屏就要翻好几页的无意义log,要知道,大量的console.log也是会影响首屏加载性能


在之前,我通过husky + lint-staged进行过限制,但还是有人以我这个有用这之前不是我写的等等诸多借口绕过了eslint检测,提交了无意义的log。所以这次我最终还是决定,你不仁就休怪我不义,TerserPlugin drop_console走起,本地开发你随便log,只要发到线上我就删掉。


{
plugins: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
pure_funcs: ["console.log", "console.info"],
},
},
})
]
}

毕竟删掉几句console.log,也算瘦身
















接着就webpack-bundle-analyzer走起,vue-cli内置的使用方式是


vue-cli-service build --report

打包后会在你输出的目录里边生成一个report.html,当时的截图找不到了,用语言描述一下就是:从node_moduels里打进去的依赖包,面积直接占了整个屏幕的大概三分之一。那个图网上很容易找到,内容就是打包产物按照体积和来源绘制成一个个的矩形在页面里。


这其实是好事,打进去的依赖包多,我们的可操作空间就大,先拿Vue开个刀。


// vue.config.js
module.exports = {
// ...
configureWebpack: {
externals: {
vue: "Vue",
},
},
// ...
}

也不要忘了把package.json里的vue依赖删除掉、在/public的模版HTML中,通过<script>引入CDN文件。


再打个包看看效果:


ac297e6a4499324cbbf2090d5c9b72d.png

可以看到vue确实咩有了


但在调试的过程中,发现第三方CDN不稳定,时而获取超时


a2e3ed8dfb40b5eb9d36693029e55e9.png

为了保险起见,只得把CDN文件copy到本地/public里来(我司没有自己的CDN或者依赖私仓,正在筹备中)


暂时没什么问题了,下一个就是我们的UI组件库ant-design-vue@1.7.8,按照相同的方式配置一下,不过这次运行后有报错了:


f4f2f6a9b9a3afaaa82b637617803dc.png

可以看到报错是和moment有关系,在antd的文档也找到了原因:如果使用已经构建好的文件,要自行引入moment



为什么antdv不做按需引入?原因有二:



  1. 项目的入口main.js中全量导入了antdv进行注册,页面中直接使用。如果要改成按需引入,要么每个页面里新增按需引入的语句,要么统计使用了哪些组件在main.js里改为按需引入(似乎有plugin解决这个问题,记不太清了)
  2. 按需的这个需,基本等于全量了。。。粗略的扫了一下文档,除了像CommentMentions这种带有互动性质的组件,其他的基本都用上了,所以改按需好像意义也不大


moment的时候国际化有一个小问题,CDN网站一般会提供以下几种文件:



  1. 无国际化的moment主体文件
  2. 带全部语言包的moment主体文件
  3. 单个语言包文件(无功能)

如果没有国际化的需求,那是万万没有必要引入全部语言包的moment。但moment默认是英语,至少需要引入一个中文语言包。碰巧antdv也需要做国际化处理,是相同的问题。


momentantdv的国际化方式很相似:


<a-config-provider :locale="antdLocale" />

moment.locale(momentLocale);

data() {
return {
antdLocale,
momentLocale
}
}

我们只需要知道这个locale运行时的值,把它提取出来就行了。打印后发现其实就是个很简单的key-value对象(不是JSON),在node_modules中的源码里找到 它们复制出来在/public下新建zhCN文件:


window.momentLocale = xxx /* 复制出来的对象 */
window.antdLocale = xxx /* 复制出来的对象 */

image.png


image.png


使用时:


<a-config-provider :locale="antdLocale" />

moment.locale(momentLocale);

data() {
return {
antdLocale: window.antdLocale,
momentLocale: window.momentLocale
}
}

以后如果有别的依赖也有类似的国际化需求,继续向zhCN.js里添加就行。只需要新增一个http请求,就解决了所有依赖的国际化问题。


剔除了antdvmoment之后的report.html


55b3f51ed28b6eb71e2939a26d8e471.png


惊喜的发现,antdvicons也被一起干掉了。


少了这么几个大家伙,此时必须要Jekins上build一波看看效果!


还记得之前把Node给升到20了吗


于是就...报错了...Node版本太低...


image.png


本地切回NodeV14,发现连server也起不来了。。


摸黑前行


预警:这将是一段枯燥且艰难的黑暗时光


搞过的都知道,处理Node版本兼容问题时,如果是需要升级还好;如果是要降级,Node内置的各种包会出现稀奇古怪的报错,而且这些报错还难以trace...



由于这趴的问题实在过于稀奇,甚至在google上都搜不到有用的信息,所以基本都没有截图,但我会尽可能的描述出我对问题的看法。看文字也许你觉得云淡风轻解决起来很轻松,但实际上花费了我接近一整天的时间以及一撮撮掉落的头发...



1. npm run server出现大量的.vue单文件报错


具体的报错信息记不太清,但报错顺序与路由表注册的顺序相符(和动态路由懒加载是两码事,路由懒加载是在运行时访问到页面才会加载对应的chunk,但编译打包时,只要是代码中webpacktrace到的文件,都会被处理)。目测是所有的.vue都有报错,那问题就应该不是出在代码上,而是整体配置上。


翻看vue-loader文档时看到了这个


image.png

升级vue-cli时确实也升级了vue-loader,按照指引配置了下,resolve


2. jsx语法报错


这个问题就有点奇怪了,在升级前,是没有给webpack做过什么支持jsx语法的配置的。升级后,却都报错了。


翻阅了一些资料和支持jsx的解决方案,大部分都是说把SFC<script>加上lang="jsx",里边的内容全部当作jsx解析。这种方式对eslintbabel的配置改动比较大,曾数次尝试无法成功,最后都把所有配置还原重新开始。


image.png


后来灵光一闪,不如直接用刚更新的vue-cli创建一个新项目,看是否支持,如果可行的话,直接把各个配置文件照搬即可。


4799c859068eaebc4f6f4a8db07ac02.png

9221f6f9f1e017649f47b29be1ab01d.png


结果还真可以。babel.config.jsvue.config.js以及package.jsoneslintConfig字段,先全部按照官方脚手架的配置改掉,成功启动之后再挨个把我们自定义的配置添加回去。这过程当中没有出现什么问题,且按下不表,resolve


3. 启动之后,页面白屏报错:(0 , vue__WEBPACK_IMPORTED_MODULE_0__.resolveComponent) is not a function


其中resolveComponent也有可能是其他一些Vue3暴露出来的Api,通过打断点观察,推测是Vue内部在初始化的时候出了问题。


不确定是哪里出了问题,但在把之前删除的Vue依赖安装回来(只是开发环境会用到,打包不会打进去)以及把添加的VueLoaderPlugin去掉以后,resolve


迎接黎明


以上这些问题解决以后,已经可以正常启动、打包项目了。但刚才的bundle analyzer进行到一半还没结束,图中的第三方依赖库应该还有一些可以剔除掉的,比如隐藏在一个业务代码chunk里的echarts


7ae1cd4a1c4f0715606a5074554c2a7.png


检索了代码后,发现有按需引入的:


import {xxx, xxx} from "echarts"

也有全量引入的:


import * as echarts from 'echarts';

在分析代码后整理了所使用到的echarts Api和组件,把全量引入改为按需引入,重新打包后发现包体积没有变。我好奇难道echarts只要有一个地方使用了按需引入,其他地方也能自动分析把全量引入改为按需引入?遂把按需引入的也反向改为全量引入重新打包,结果:


5010aca8d7ff78da92b22705416a4c1.png


第二次的改动体积变化了,那就只能说明....


问了写那段代码的同事,果然,在需求迭代的过程当中技术方案变更了,所以那个文件废弃掉没有用了,改了个寂寞...


此时还剩下jquerylodash计划剔除掉,其他的依赖包有一部分已经是比较规范的按需引入,剔除掉改为cdn引入带来的收益不大,当然主要的原因还是因为


jquery:这个npm包有点意思


5794b48e2187021280c39b9a6122da5.png


打进来的是非压缩版本,因为package.json中设置的main就确实是这个,但dist包中明明提供了压缩后的版本。两个版本的体积差距在三倍多,不知包作者的意图是什么


1ed025730a7a1ba28ef75a190cb696e.png


298e66d7e62306a1dc18b97e6b10ac5.png


但最后还是把jquery这个依赖彻底放弃了:整个项目中只有一个远古时期添加的图片预览组件依赖了它,而我们现在开发了样式、功能更为强大的新组件,所以把所有使用到这个组件的地方都改为使用新组件,然后顺带把jquery uninstall了。


lodash:官网本身提供了可按需引入的版本lodash-es,但项目中太多地方都是全量引入的方式在使用


import * as _ from "lodash"

暂且先改成CDN的方式全量引入


至此,bundle analyzer的分析图变成了这样:


8920151ddcf9709147f3a9ab8e094ec.png


三方依赖的chunk已经比包含了echarts的那个业务代码chunk体积还要小。瘦身瘦到这里感觉差不多了,那些更小的依赖包本身体积不大,换成一个http请求也未必是一件划算的事。


然后就还是回到webpack的配置上来,前边一直在琢磨怎么添加配置去做优化,但vue-cli本身已经封装了一套久经考验的配置,不如从这个配置着手,看能否针对我们项目的实际情况做一些修改


获取配置命令(融合了自定义的配置)


vue inspect --mode=production > file-name.js

mode不传的话默认是development。下载下来打开,1400多行猛的一看似乎有点唬人,但实际上有1000行左右都是对样式文件的loader配置。


image.png


粗略的看下vue-cli@5.0.8中有哪些值得注意的配置



  • 解析文件的优先级

// 导入模块时如果不提供文件后缀,同名文件 后缀名的优先级
extensions: [".mjs", ".js", ".jsx", ".vue", ".json", ".wasm"]


  • Hash

optimization: {
realContentHash: false, // 使用非严格的hash计算,减少耗时
}


  • 代码压缩:css使用的是CssMinimizerPluginjs使用的是TerserPlugin

minimizer: [
// 已经内置了js压缩工具terser
new TerserPlugin({
terserOptions: {
compress: {
arrows: false,
collapse_vars: false,
comparisons: false,
computed_props: false,
hoist_funs: false,
hoist_props: false,
hoist_vars: false,
inline: false,
loops: false,
negate_iife: false,
properties: false,
reduce_funcs: false,
reduce_vars: false,
switches: false,
toplevel: false,
typeofs: false,
booleans: true,
if_return: true,
sequences: true,
unused: true,
conditionals: true,
dead_code: true,
evaluate: true,
},
mangle: {
safari10: true, // 代码混淆时兼容使用`let`关键字声明的循环迭代器变量可能会出现无法重复声明let变量的错误。
},
},
parallel: true, // 多进程打包
extractComments: false, // 不将注释单独提取到一个文件中
}),
new CssMinimizerPlugin({
parallel: true,
minimizerOptions: {
preset: [
"default",
{
mergeLonghand: false,
cssDeclarationSorter: false,
},
],
},
}),
]


  • Loader

    • 大量的篇幅编排不同样式文件相关的Loader,分别有csspostcssscsssasslessstylus,按照css moduels in SFC -> SFC style -> normal css modules -> normal css的顺序依次处理。
    • 对于脚本文件,已经开启了多线程转译以及babel缓存功能



{
test: /\.m?jsx?$/,
exclude: [
function () {
/* omitted long function */
},
],
use: [
{
loader:
"path-to-your-project/node_modules/thread-loader/dist/cjs.js",
},
{
loader:
"path-to-your-project/node_modules/babel-loader/lib/index.js",
options: {
cacheCompression: false,
cacheDirectory:
"path-to-your-project/node_modules/.cache/babel-loader",
cacheIdentifier: "1d489a9c",
},
},
],
}


  • Plugin

    • VueLoaderPlugin:已经内置了
    • DefinePlugin:注入编译时的全局配置
    • CaseSensitivePathsPlugin:路径的大小写严格匹配
    • FriendlyErrorsWebpackPlugin:优化报错信息
    • MiniCssExtractPlugin
    • HtmlWebpackPlugin
    • CopyPlugin:配置了info.minimized = true,copy的同时也会压缩
    • ESLintWebpackPlugin:同样开启了缓存



得,不仅没找到有啥可优化的地方,甚至还污染了人自带的配置:


已经内置了TerserPlugin,前边为了打包时去除consoleplugin里边又配置了一次,通过speed-measure-webpack-plugin分析时发现似乎是走了两遍TerserPlugin


只好通过webpack-chain去注入一下,顺便把项目中其他修改webpack配置的地方也改为注入的形式。(使用ConfigureWebpack去改,无法改到已有的TerserPlugin配置):


chainWebpack: (config) => {
config.when(process.env.NODE_ENV === "production", (config) => {
config.devtool(false);
config.optimization.minimizer("terser").tap((args) => {
const compress = args[0].terserOptions.compress;
args[0].terserOptions.compress = {
...compress,
drop_console: true,
pure_funcs: ["console.log", "console.info"],
};
return args;
});
});
config
.externals({
vue: "Vue",
moment: "moment",
"moment/locale/zh-cn": "moment.local",
"ant-design-vue": "antd",
lodash: "_",
})
.resolve.alias
.set("@", path.join(__dirname, "src"))
.set("@worker", path.resolve(__dirname, "public/worker.js"))
.end();
config.plugin("speed-measure").use(SpeedMeasurePlugin);
}

image.png


如果使用ConfigureWebpack


configureWebpack: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
pure_funcs: ["console.log", "console.info"],
},
},
}),
],
},

集成的配置最下方会出现一个新的minimizer数组,不是我们想要的效果


image.png




截止到目前,构建速度变成这样(果然还是没有变更快)


image.png


从项目剔出去的第三方依赖,体积是这么多


928af467649ccc1467ac6476c0d230e.png


不过虽然没打进chunk里去,但还是作为静态依赖在构建产物中,下一步就是搭一个公司内部的简易缓存服务器(有关缓存的内容可以看我上一篇文章)。届时这部分体积才算真正从项目里移除了,不过此时我们还是可以把它视作优化的成果,由于没有对别的类型的资源做什么优化处理,也压根就没什么别的资源,所以只看打包后的js体积的话:


image.png


优化前








image.png


优化后


数据也基本对的上,所以综合来看:



  • 平均构建速度(average full time):从5分50秒减少到54秒,优化率84.48%1 - 54秒 / 3分50秒
  • 脚本构建体积(script size):从5.5M减少到3.6M,优化率35.55%1 - 3.6M / 5.5M





先这样吧,至少下次被问到webpack,多少有点自己的东西能讲,近期可能也不会再更新文章了,原因嘛,你们懂的


image.png


欢迎真诚交流,但如果你来抬杠?阿,对对对~ 你说的都对~


作者:Elecat
来源:juejin.cn/post/7389044903940603945

0 个评论

要回复文章请先登录注册