
JavaScript压缩:删除了什么,实际能节省多少
📷 Pixabay / PexelsJavaScript压缩:删除了什么,实际能节省多少
变量重命名、死代码消除、Tree Shaking——压缩JS时发生了什么,以及什么时候使用在线工具就够了。
网络上每个字节都有意义。我知道这听起来像是在性能博客上读到后立刻忘掉的那种话,但我已经因此吃过足够多的亏,以至于真的把它当回事了。几年前,我在调试为什么一个看似简单的营销页面感觉迟缓——结果发现有三个未压缩的jQuery插件,总共340KB。压缩后降到了98KB。在快速连接上这不是什么戏剧性的数字,但在移动设备上?天壤之别。
所以让我们来谈谈JavaScript压缩——它实际上做了什么、不做什么,以及什么时候简单的在线工具是正确的选择,什么时候需要设置构建管道。
压缩实际删除了什么
压缩从JS文件中剥除JavaScript引擎不需要执行代码的字符。最常见的删除内容:
注释。 单行(// ...)和多行(/* ... */)注释都会被删除。对于注释丰富的代码,这通常是最大的收益。
空白和换行符。 用于缩进的空格、函数之间的空行、=和+等运算符周围的空格。
可选分号。 一些压缩器在JavaScript的自动分号插入(ASI)使分号冗余的地方删除它们。这是某些情况下可能出现问题的地方——稍后会详细介绍。
高级:变量重命名。 更强大的压缩器(Terser、UglifyJS)还会将描述性名称如currentUserIndex的局部变量重命名为单个字母如a。这不仅仅是关于字符数——它还使代码更难以逆向工程。
JS压缩工具处理基本操作:删除注释和折叠空白。它不会重命名您的变量,老实说,对于大多数快速工作,这已经足够了。
实际能节省多少大小?
这很大程度上取决于代码。让我给您一些实际数字:
注释丰富的工具代码: 200KB → 95KB(减少52%)——节省巨大,因为注释可能占文件的一半。
框架源代码(已优化): 150KB → 130KB(减少13%)——已经很精简,没有太多可剥离的。
普通应用代码: 50KB → 32KB(减少36%)——这是一个合理的参考值。
真正的问题是:大小对您的具体情况重要吗?如果您提供的是3KB的实用脚本,压缩可能节省大约1KB。这不是没有意义,但也不值得为此设置构建系统。如果您在交付500KB的应用逻辑,压缩绝对重要。
除文件大小外还有一个隐藏好处:解析时间。更少的文字意味着JavaScript引擎可以更快地解析文件。在低端Android设备上,这可以在交互时间上产生明显差异。
基本压缩的局限性
实话实说。JS压缩工具和大多数基于正则表达式的压缩器有真实的局限性:
它们无法安全地处理所有内容。 JavaScript有语法边缘情况。基于正则表达式的注释删除可能会意外损坏包含//或/*模式的模板字面量,或包含类似注释序列的字符串。
无变量重命名。 文件因空白删除而变小,但您的变量名保持不变。对于紧密优化,您需要Terser。
无死代码消除。 高级工具可以检测到您导入了一个函数但从未调用它,并删除整个内容。基本压缩器无法做到这一点。
无Tree Shaking。 现代打包工具可以从您的包中消除未使用的整个模块。这不完全是压缩,但相关且更强大。
那么什么时候应该使用基本的在线压缩工具?
- 您有一个小型、自包含的脚本(分析代码片段、小部件嵌入代码)
- 您在没有构建过程的静态HTML页面上工作
- 您想快速检查特定代码片段的压缩后外观
- 您继承了没有构建工具的遗留项目,今天不打算添加它们
对于有构建过程的任何项目——React应用、Vue、Svelte,甚至简单的Webpack设置——让构建工具自动处理压缩。您不应该将代码复制粘贴到在线工具作为CI/CD管道的一部分。
出问题时
我见过几种基本压缩破坏代码的情况:
依赖ASI。 像这样的代码:
return
{
value: 1
}
由于ASI(引擎在return后插入分号),这在JavaScript中返回undefined。积极删除换行符的压缩器可能会将其变成return{value:1}——这实际上是正确的,并返回对象。但其他对ASI敏感的模式可能会产生相反的效果。
Eval或Function构造函数。 在运行时构造和评估函数字符串的代码与变量重命名不兼容(因为运行时字符串仍使用原始名称)。基本空白压缩在这里通常是安全的,但要小心。
正则表达式字面量与除法运算符。 基于正则表达式的压缩器有时会将/pattern/flags与除法运算符混淆。好的压缩器可以处理这个问题;粗糙的压缩器可能会破坏您的正则表达式。
经验法则:压缩后运行您的测试。如果出了问题,检查出问题区域附近的压缩输出。
将压缩与其他优化配合使用
压缩作为更广泛性能策略的一部分效果最好:
Gzip/Brotli压缩。 您的Web服务器在发送文件之前应该压缩它们。即使是压缩过的JavaScript也能很好地压缩,因为重复模式的存在。压缩代码与HTTP压缩的组合通常可以将文件大小从原始减少60-80%。
打包。 多个小JS文件有HTTP请求开销。使用esbuild或Webpack等工具将它们打包成一个文件(打包)可能比单独的压缩更有影响力。
代码分割。 不要向只需要50KB的用户提供500KB的JS。通过动态导入进行懒加载和代码分割意味着您只为当前页面提供需要的内容。
Defer/Async加载。 将defer或async添加到您的<script>标签可以防止JS阻塞HTML解析。延迟加载的200KB脚本比阻塞的50KB脚本有更好的用户体验。
如果您也在压缩CSS,请查看CSS压缩工具。对于HTML,有HTML压缩工具。它们合在一起可以从页面的总传输大小中削减相当多的内容。
使用在线JS压缩工具
JS压缩工具使用很简单:
- 将您的JavaScript粘贴到输入字段
- 点击压缩以删除注释和空白
- 或点击格式化来美化已压缩的代码(当您从某处获得压缩包并想读取它时很有用)
- 检查压缩前后的字节大小和节省百分比
- 复制输出
一切都在您的浏览器中运行——您的代码永远不会离开您的机器。这对于包含API密钥或您不想粘贴到随机第三方服务的业务逻辑的代码来说很重要。
关于混淆的说明
有些人将压缩与混淆混为一谈。它们相关但不同:
- 压缩使代码更小,传输更快
- 混淆使代码更难阅读和理解
Terser的变量重命名作为压缩的副作用是轻度混淆。真正的混淆器走得更远——将所有内容重命名为_0x1a2b、插入死代码、编码字符串。这不会提高性能(它通常会使代码更大),也不能防止坚定的逆向工程。它只是提高了所需的努力。
如果您试图保护客户端代码,混淆是减速带,而不是墙。真正的答案是:不要在客户端JavaScript中放置秘密。
总结
压缩是一个小但真实的胜利——对于用户实际下载的任何代码都值得做。对于快速工作,在线工具确实是正确的工具:无需设置、无依赖,只需粘贴然后使用。对于有构建过程的生产级代码,让构建工具自动处理。
JS压缩工具很好地处理了常见情况。对于深度优化——变量重命名、Tree Shaking、死代码消除——您需要Terser或现代打包工具。两者各有其用武之地。
最后一点:始终保留未压缩的源代码。您无法可靠地将代码反压缩回可读形式(格式化工具可以添加空白,但被重命名为a、b、c的变量名已经消失了)。保留您的源代码,将其提交到版本控制,让构建过程生成压缩输出。