当前位置: 首页 > article >正文

Terser 与 Esbuild 比较


参考链接

1. esbuild
2. terser

前言

Terser 为什么比 esbuild 压缩体积更小?原理分析

terser 之所以比 esbuild 产生的打包体积更小,主要是因为 它提供了更高级的优化手段,包括 作用域折叠(Scope Hoisting)、变量提升、代码混淆、AST 级别优化、更多高级压缩策略,而 esbuild 的压缩主要是 简单的语法转换和删除无用空格/换行符,缺少深入的 AST 级别优化。

下面我们从 代码优化原理、构建方式、作用域分析、Tree Shaking 等方面进行深入分析。


1. AST 层面的压缩差异

AST(Abstract Syntax Tree)抽象语法树

terseresbuild 都基于 AST(抽象语法树)进行代码优化,但 terser 在 AST 级别执行了更多高级优化,而 esbuild 主要做基本的 minify(缩小代码)。

示例

假设我们有这样一段 JavaScript 代码:

function add(a, b) {
  return a + b;
}

console.log(add(1, 2));

Esbuild 压缩

详细分析

1. 词法分析(Lexical Analysis)
  • esbuildlexer 会解析代码,将其转换成 Token 流,例如:
    function → Token{Type: FUNCTION}
    add → Token{Type: IDENTIFIER}
    ( → Token{Type: PUNCTUATION}
    a → Token{Type: IDENTIFIER}
    b → Token{Type: IDENTIFIER}
    return → Token{Type: RETURN}
    a + b → Token{Type: BINARY_EXPRESSION}
    
  • 这个过程只是将代码拆分成可解析的最小单元。
2. 语法解析(Parsing)
  • esbuildjs_parser 中将 Token 解析为 AST(抽象语法树)
    {
      "type": "FunctionDeclaration",
      "name": "add",
      "params": ["a", "b"],
      "body": {
        "type": "ReturnStatement",
        "argument": {
          "type": "BinaryExpression",
          "operator": "+",
          "left": "a",
          "right": "b"
        }
      }
    }
    
  • console.log(add(1, 2)) 也被解析成 AST 结构。
3. 代码优化(Minification)

js_printer 处理阶段:

  • 移除空格、换行
  • 保留 function add 结构(因为 esbuild 不是一个高级压缩工具)
  • 不会执行函数折叠(不会把 add(1,2) 直接计算成 3

所以最终输出:

function add(a,b){return a+b}console.log(add(1,2));

为什么 esbuild 没有优化成 console.log(3);

相比 terseresbuild 不会执行高级优化,例如:

  • 常量折叠(Constant Folding):计算 add(1, 2) 并替换成 console.log(3)
  • 函数内联(Inlining):如果 add() 只在一个地方调用,它可以被展开成 console.log(1 + 2)
  • Dead Code Elimination(DCE,死代码消除):如果 add() 没有被调用,它会直接删除 add()

terser 会进行这些优化:

  • terser 发现 add(1,2) 这个调用是 纯函数(pure function),可以直接计算并替换为 console.log(3),去掉 add 函数。
console.log(3);

关键优化点:

  • terser 直接 折叠函数调用结果(常量折叠),消除了 add() 这个函数,从而减少代码体积。

2. Terser 的高级优化机制

1) 作用域折叠(Scope Hoisting)

作用域折叠可以 减少作用域的嵌套,将函数或变量合并到更紧凑的作用域中。

示例

原始代码:

function outer() {
  function inner() {
    console.log('Hello');
  }
  return inner;
}
outer()();
esbuild 压缩
function outer(){return function(){console.log("Hello")}}outer()();
  • 依然保留了 outer 这个作用域。
terser 压缩
console.log("Hello");
  • terser 直接移除了 outer,因为它的唯一作用是返回 inner,可以省略掉。

作用:

  • 减少作用域层级,降低闭包开销,提高运行时性能。

2) 变量提升和合并

terser 会尽可能减少变量声明的数量,合并多个变量定义,从而减少代码大小。

示例

原始代码:

let a = 1;
let b = 2;
let c = a + b;
console.log(c);
esbuild 压缩
let a=1,b=2,c=a+b;console.log(c);
  • esbuild 只是合并了 let 语句,但没有进一步优化。
terser 压缩
console.log(3);
  • terser 发现 c 是常量,直接替换掉,省略了变量声明。

3) 常量折叠(Constant Folding)

terser 能够分析并消除计算结果为常量的代码,而 esbuild 没有类似的优化。

示例

原始代码:

const a = 100;
const b = a * 2;
console.log(b);
esbuild 压缩
const a=100,b=a*2;console.log(b);
terser 压缩
console.log(200);
  • terser 发现 b 是常量,直接用 200 替换掉 b,从而减少代码体积。

4) 死代码消除(Dead Code Elimination)

terser 可以彻底删除不会被执行的代码,而 esbuild 只能删除一些最基本的未引用变量。

示例

原始代码:

function test() {
  if (false) {
    console.log("This will never run");
  }
}
test();
esbuild 压缩
function test(){}test();
  • esbuild 只删除了 if 语句,但 test() 这个函数还在。
terser 压缩
  • terser 发现 test() 没有任何作用,直接删掉整个函数调用。

5) Tree Shaking(摇树优化)

Tree Shaking 主要是用于删除未使用的模块terser 在这方面比 esbuild 更激进。

示例

原始代码:

import { unusedFunc, usedFunc } from './module.js';
usedFunc();
esbuild 压缩
import { usedFunc } from "./module.js"; usedFunc();
  • esbuild 只是去掉了 unusedFunc 的导入,但代码本身依然保留 import 语句。
terser 压缩
import { usedFunc } from "./module.js"; usedFunc();
  • terser 在与 rollup 结合时,可以进一步优化 整个 import 语句,如果 usedFunc 也可以被内联,则可能直接删除 import

3. esbuild 的设计取舍

esbuild 的目标是快速打包,而不是极致的压缩,因此做了一些权衡:

  • 不做复杂的 AST 分析(导致一些优化缺失)
  • 不执行复杂的代码混淆(可读性更强)
  • 优先优化构建速度(10~100 倍快)

相比之下,terser 作为一个专业的压缩器,使用了:

  • 深度 AST 分析
  • 更激进的 Tree Shaking
  • 代码混淆
  • 优化作用域和常量折叠

这些优化让 terser 产出的代码体积更小,但牺牲了构建速度。


4. 总结

特性esbuildterser
构建速度快(10-100 倍)
代码压缩基本压缩(去空格、缩变量名)深度优化(作用域折叠、Tree Shaking、死代码消除)
Tree Shaking一般更激进
变量合并基本合并常量折叠+变量内联
代码混淆支持
适用场景开发环境 & 速度优先生产环境 & 体积优化优先

如果你的项目 构建速度是瓶颈,继续使用 esbuild;如果 最终代码体积更重要,建议切换到 terser 进行压缩优化。


http://www.kler.cn/a/561118.html

相关文章:

  • 如何在本地和服务器新建Redis用户和密码
  • 大数据开发平台的框架
  • 【python】提取word\pdf格式内容到txt文件
  • 自动驾驶中planning为什么要把横纵向分开优化?
  • Unity Shader 学习13:屏幕后处理 - 使用高斯模糊的Bloom辉光效果
  • Spring Boot 整合原生的 mybatis
  • C++与Python实现LiDAR点云投影对比:关键差异与易错点详解
  • TVS管学习记录
  • 趣解http和https各自的原理以及它们的区别
  • Unity Mixamo模型更好的适配角色模型
  • 设计模式觉醒系列(01)设计模式的基石 | 六大原则的核心是什么?
  • 二级公共基础之数据库设计基础(一) 数据库系统的基本概念
  • 基于数据可视化学习的卡路里消耗预测分析
  • 计算机毕业设计SpringBoot+Vue.js古典舞在线交流平台(源码+文档+PPT+讲解)
  • 在Spring Boot+Vue前后端分离的项目中使用JWT实现基本的权限校验
  • 2步本地安装部署国产之光大模型DeepSeek,附Mac安装教程和安装包!
  • Oracle Fusion Middleware更改weblogic密码
  • AI手机的技术细节
  • Brave132编译指南 Linux篇 - 构建与运行(七)
  • 安装Redis并把Redis设置成windows下的服务然后进行Redis实例演示