JavaScript 代码压缩原理与 UglifyJS 实战
本文深入解析 JavaScript 代码压缩的核心原理,涵盖移除空白字符、变量名混淆及布尔值简化等策略。通过引入抽象语法树(AST)概念,阐明 UglifyJS 与 Babel 的工作机制,并展示如何在 Webpack 中利用 TerserPlugin 进行生产环境代码优化。
前端代码压缩的必要性
随着前端技术的演进,尤其是 React 和 Vue 等框架构建单页应用(SPA)的普及,前端应用的功能日益强大,项目复杂度也随之显著增加。在此背景下,前端静态资源的体积不断膨胀,其中 javascript 资源已成为前端项目的主体。因此,对这些资源进行体积压缩变得至关重要。
减小代码体积具有多重重要意义:对于用户而言,更小的文件体积意味着更快的加载速度和更流畅的使用体验,这有助于提升用户留存率并间接增加企业收益;对于服务器端,较小的资源体积能够降低带宽消耗,从而减少服务器运营成本。
在前端构建和编译过程中,开发者通常可以使用 webpack 配置中的 optimization.minimizer 选项来自动执行代码压缩与优化。然而,深入理解代码压缩背后的实现机制,有助于在生產环境进行控制台调试时,对报错信息和代码结构有更深刻的理解。
查看资源体积的方法
如何查看资源的体积
我们编写的代码在操作系统中表现为具体的文件。通过文件系统提供的 stat 信息,我们可以准确查看该文件所占用的存储空间大小。
stat 命令用于打印文件的详细元数据信息:
$ stat config.js
File: ‘config.js'
Size: 3663 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 806060 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2020-02-13 13:43:54.851381702 +0800
Modify: 2020-02-13 13:43:52.668417641 +0800
Change: 2020-02-13 13:43:52.691417262 +0800
Birth: -
stat 命令输出的信息较为详尽,若仅需衡量文件体积,使用 wc -c 命令更为便捷。
$ wc -c config.js
3663 config.js
代码压缩的基本策略
如何压缩代码体积?
去除多余字符: 空格,换行及注释
// 对两个数求和
function sum (a, b) {
return a + b;
}
首先将抽象问题具体化。以一段示例代码为例,探讨其体积压缩的具体方法:
此时文件大小是 62 Byte , 一般来说中文会占用更大的空间。
多余的空白字符(如空格、换行符)以及注释会占用大量文件体积。当移除所有空白字符和注释后,代码体积将显著减少。
去掉多余字符之后,文件大小已经变为 30 Byte 。 压缩后代码如下:
function sum(a,b){return a+b}
替换掉多余字符后会有什么问题产生呢?
确实存在潜在问题,例如将多行代码压缩至单行时,需特别注意行尾分号的处理。这一问题需要通过下文介绍的 AST(抽象语法树)技术来解决。
变量名压缩与作用域处理
压缩变量名:变量名,函数名及属性名
function sum (first, second) {
return first + second;
}
如以上 first 与 second 在函数的作用域中,在作用域外不会引用它,此时可以让它们的变量名称更短。但是如果这是一个 module 中, sum 这个函数也不会被导出呢?那可以把这个函数名也缩短。
// 压缩: 缩短变量名
function sum (x, y) {
return x + y;
}
// 再压缩: 去除空余字符
function s(x,y){return a+b}
在这个示例中,当完成代码压缩 ( compress ) 时,代码的混淆 ( mangle ) 也捎带完成。 但此时缩短变量的命名也需要 AST 支持,不至于在作用域中造成命名冲突。
代码简化与 AST 技术
更简单的表达:合并声明以及布尔值简化
合并声明的示例如下:
// 压缩前
const a = 3;
const b = 4;
// 压缩后
const a = 3, b = 4;
布尔值简化的示例如下:
// 压缩前
!b && !c && !d && !e
// 压缩后
b||c||d||e
这个示例更是需要解析 AST 了
抽象语法树(AST)的应用
AST
AST ,抽象语法树,js 代码解析后的最小词法单元,而这个过程就是通过 Parser 来完成的。
那么 AST 可以做什么呢?
- eslint: 校验你的代码风格
- babel: 编译代码到 ES 低版本
- taro/mpvue: 各种可以多端运行的小程序框架
- GraphQL: 解析客户端查询
我们在日常工作中经常会不经意间与它打交道,如 eslint 与 babel ,都会涉及到 js 与代码中游走。不同的解析器会生成不同的 AST,司空见惯的是 babel 使用的解析器 babylon ,而 uglify 在代码压缩中使用到的解析器是 UglifyJS 。
你可以在 AST Explorer [3] 中直观感受到,如下图:
那压缩代码的过程:code -> AST -> (transform)一颗更小的 AST -> code,这与 babel 和 eslint 的流程一模一样。
UglifyJS 与 Webpack 集成实战
UglifyJS
不要重复造轮子!
于是我找了一个久负盛名的关于代码压缩的库: UglifyJS3 [4] ,一个用以代码压缩混淆的库。那它是如何完成一些压缩功能的,比如替换空白符,答案是 AST。
webpack 中内置的代码压缩插件就是使用了它,它的工作流程大致如下:
// 原始代码
const code = `const a = 3;`
// 通过 UglifyJS 把代码解析为 AST
const ast = UglifyJS.parse(code);
ast.figure_out_scope();
// 转化为一颗更小的 AST 树
compressor = UglifyJS.Compressor();
ast = ast.transform(compressor);
// 再把 AST 转化为代码
code = ast.print_to_string();
而当你真正使用它来压缩代码时,你只需要面向配置编程即可,文档参考 uglify 官方文档 [5]
{
{
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
}
}
在 webpack 中压缩代码
在知道代码压缩是怎么完成的之后,我们终于可以把它搬到生产环境中去压缩代码。终于到了实践的时候了,虽然它只是简单的调用 API 并且调调参数。
一切与性能优化相关的都可以在 optimization 中找到, TerserPlugin 是一个底层基于 uglifyjs 的用来压缩 JS 的插件。
optimization: {
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
sourceMap: true
})
]
}
参考资料 [1]
shfshanyue/blog: https://github.com/shfshanyue/blog
[2]
前端工程化系列: https://github.com/shfshanyue/blog/tree/master/frontend-engineering
[3]
AST Explorer: https://astexplorer.net/
[4]
UglifyJS3: https://github.com/mishoo/UglifyJS2
[5]
uglify 官方文档: https://github.com/mishoo/UglifyJS2#parse-options
到此这篇关于javascript 代码是如何被压缩的的文章就介绍到这了,更多相关js代码压缩内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
Mock.js 与 MSW、YApi 对比:前端接口模拟工具怎么选
2026-11-04 22:55:05
Turf.js 安装与引入:NPM 打包及 CDN 快速上手指南
2026-11-03 23:23:32
JS代码混淆后无法调试?Source Map配置与浏览器断点技巧
2026-11-03 14:12:17
前端JS发送HTTP请求遇到跨域(CORS)报错怎么解决
2026-11-02 10:30:07
Three.js怎么安装:npm引入与CDN加载方法
2026-11-02 09:23:52
