代码的大小取决于最小化器,收集器和语言。 意外的Webpack更新

我叫Ilya Goldfarb,我是Yandex接口的开发人员。 我对跟踪前端构建工具的开发方式很感兴趣,因此我尝试研究每种流行解决方案的变化。

考虑到webpack的第五版,我想谈谈其2018年11月19日的次要发行版4.26.0,其中,意外地且未宣战的情况下,更改了默认版本的minifier。 它曾经是一个UglifyJS软件包,现在使用Terser,它是UglifyES的一个分支,它是UglifyJS的一个分支,可以压缩ES5和ES6代码。 当主要维护者拒绝支持和开发UglifyES时,Terser出现了。 但是,UglifyJS在最后一个版本发布时也于2018年8月停止了开发。 在新分支中,我们修复了一些错误,并对代码进行了一些重构。

这些缩小器的API是兼容的,但是它们产生不同的压缩结果。 通常,此级别的更改仅发生在主要更新中,而不是次要更新中。 因此,许多开发人员可能不会关注创新。 当然,在大多数情况下,所有内容都可以使用,但是没有人愿意成为由于构建和缩小系统而在项目生产中遇到错误的人。

这整个故事促使我对压缩进行了一些个人研究。 这是我问的问题:

  • 什么可以更好地压缩ES5,TerSer或UglifyJS?
  • 什么是加载速度更快:来自Terser或UglifyJS的ES5压缩版本?
  • 哪个版本的权重更大:ES5或ES6? TypeScript如何影响这一点?
  • 默认设置和手动设置之间有很大区别吗?
  • 如果不是webpack? 谁生产较小的程序集,汇总或Webpack?

为了进行研究,我制作了一个小型的React 16应用程序,该应用程序呈现了Vue 2应用程序,该应用程序呈现了具有一个完整按钮的Angular 7应用程序。

总共释放了3 529 695字节的非最小代码(720 393 gzip字节)。

什么可以更好地压缩ES5,TerSer或UglifyJS?


我选择了最后一个可用的UglifyJS,并将其与带有ES5选项的Terser Webpack一起使用,并使用了相同的压缩设置。

大小(以字节为单位)
大小以字节为单位(gzip)
UglifyJS
1,050,376
285,290
特塞尔
1,089,282
292678
底线:UglifyJS的压缩效果更好,为3.5%(gzip为2.5%)。

什么是加载速度更快:来自Terser或UglifyJS的ES5压缩版本?


我使用标准的DevTools Yandex浏览器测量了性能。 我将页面加载了12次,并采用了值Scripting(脚本的运行时),但丢弃了前三个维度。
UglifyJS-221毫秒(错误2.8%)。
Terser-226毫秒(错误2.7%)。
底线:对于这样的错误,值太小,我们可以认为它们相同。 我们还得出结论,该方法不适用于测量负载时间。
我没有测量和比较代码的速度,因为不同的代码工作方式不同。 每个项目的开发人员应独立调查此问题。

哪个版本的权重更大:ES6或ES5? TypeScript如何影响这一点?


为了比较两个版本并只关注技术,我使用了Babel插件并制作了四个程序集:

  • ES5:标记为es2016的所有插件,+用于Object.assign的插件+用于更高版本的插件+实验性插件,目标安装在ES5中的tsconfig中;
  • ES5(ts esnext):所有标记为es2016的插件,+ Object.assign的插件+更高版本的所有插件+实验性插件,tsconfig中的target设置为esnext;
  • ES6:仅适用于es2017及更高版本的插件+实验性插件,tsconfig中的target设置为ES6;
  • ES6(ts esnext):仅适用于es2017和更高版本的插件+实验性插件,tsconfig中的target设置为esnext。


大小(以字节为单位)
大小以字节为单位(gzip)
ES5
1186520
322071
ES5(ts esnext)
1,089,282
292678
ES6
1,087,220
292232
ES6(ts esnext)
1,087,220
292232
底线:Babel用esnext下的编译时间代码压缩的版本减少了97238字节(8.2%)。 出乎意料的是,发生了很多事情,因为 Utglify 编写angular的TypeScript,而用JavaScript Terser 编写的Vue和React ,像Uglify一样,在用webpack编译时无法编译使用web脚本从angular传递来的未使用的代码。 这是此示例的编译错误。 在另一个项目上的组装中,可能不是,并且差异将小得多。

还可以看到,ES6的代码量仅比ES5小2062字节。 在宠物项目上,我得到了完全不同的结果:ES6代码比ES5多3-6%。 这是由于几个因素造成的,其中两个主要因素:
1.插入一次用于类继承的Babel帮助器,然后花费四个字节(e(a,b)),ES6使用本机继承,花费15个字节(类a扩展了b)。
2.声明变量的方法。 在ES5中,这些变量是var,它们可以完美压缩。 但是在ES6中,这些是let和const,它们保留初始化顺序,并且不会相互结合。

不安全的主动缩小功能(例如强制箭头功能)或使用宽松的设置将有助于减小ES6代码的大小。 小心并考虑其细微之处。 例如,在Firefox中,箭头功能比平时慢四倍 ,但是在Chromium中没有区别。

因此,不可能明确地回答这个问题:结果高度依赖于代码和目标运行时。

默认设置和手动设置之间有很大区别吗?


比较一下稍微调整设置是否可以减小文件大小。 例如,我们指示缩小必须重复五次。 默认情况下,它仅传递一次。

大小(以字节为单位)
大小以字节为单位(gzip)
Terser(默认)ES5
1,097,141
294306
Terser(通过5次)ES5
1089312
292,408
丑化(默认)ES5
1 091 350
294,845
丑化(通过5次)ES5
1,050,363
284618
底线:默认情况下,缩小五倍的Uglify小于Uglify 3.7%(gzip占3.4%)。 因此,您必须始终收紧压缩设置。 顺便说一句,最小化五倍并不意味着组装时间会延长五倍。 例如,在此测试项目中,一次缩小需要18秒,五次-38和十次-49。我建议通过实验找到项目的理想值,此后,缩小将停止并且代码不会更改。 通常是5到10。还有很多其他选择:注释:false删除所有有关许可证的注释(尽管这是一个法律问题),以及hoist_funs:真正的组功能集中在一个地方,可以更好地优化vars。 理想情况下,您需要检查所有设置

谁生产较小的程序集,汇总或Webpack?


汇总是具有内置摇树机制的备用收集器。 对于测试,我在Rollup 0.67.4上进行了构建,并使用了与Webpack相同的设置。

大小(以字节为单位)
大小以字节为单位(gzip)
汇总ES5(丑化)
990497
274105
汇总ES5(Terser)
995318
272532
webpack ES5(丑化)
1,050,363
284618
webpack ES5(Terser)
1089312
292,408
底线:Rollup和Uglify的结果少了5.6%(3.6%gzip)。

发生这种情况有几个原因:

1. Webpack包含拐角处的拐杖。 例如, 此代码将来自Object()中另一个模块的每个函数调用包装起来。 这样做是为了防止对不严格使用模块的上下文传输到不严格使用模块。 编写良好的项目没有第三方依赖性,不需要包装,但有时汇编中不仅包含编写良好的代码。 在这方面,webpack看起来更可靠。 反过来,Rollap认为所有模块都是ES6模块,并且始终严格按照使用条件执行,因此对于他来说根本不存在此问题。

一个重要的问题是这种webpack拐杖如何影响性能。 想象一下,我们编写了不需要额外包装程序的完美代码,但是每个函数调用都将通过它们。 这会增加少量的性能开销:在Chromium中,每个函数调用大约需要百分之一微秒(在Firefox中是十分之一)。

2. webpack有一个小的引导程序,用于控制模块的初始化和加载。 汇总不使用包装器,而只是将所有模块的代码放入单个作用域中。 该webpack有类似的优化,但是它不适用于所有模块。

研究总结


我希望许多人在阅读本文后,将检查他们的构建系统,并确保他们使用所有可能的技巧来获得最佳压缩效果。 快速简便。

首先,正确设置一堆TypeScript和Babel。 让程序集的每个组件都做自己的事情:一个检查类型,第二个负责转换为过时的标准。

其次,使用ES5时,您可以将Minifier改回UglifyJS,但请记住,不再支持它。

第三,最好选择汇总进行组装。 但是,由于缺少某些插件,因此并非在所有情况下都可行。 组装后,请不要忘记通过功能测试来检查功能。 如果您没有它们-是时候开始编写它们了。

Source: https://habr.com/ru/post/zh-CN438022/


All Articles