# 混淆预设

从 minify 到 extreme 的五个保护级别，每个级别设定的复杂度目标，以及零配置默认值（light）已经保证的内容。

Source: https://www.afterpack.dev/zh/docs/presets

预设是一组有名称的变换，具有已知的体积和运行时代价。更重的预设施加更多变换，但这并不是经过测量的保护提升。一个名称设定一个[复杂度目标](https://www.afterpack.dev/zh/docs/config#complexity)；输出体积由该目标决定。显式设置 [`complexity`](https://www.afterpack.dev/zh/docs/config#complexity) 会覆盖该目标，而直接设置的值总是作用于每一个 token，即使在 `light` 下也是如此。

*(preset ladder diagram — see https://www.afterpack.dev/docs/presets)*

## 阶梯

| 预设 | [`complexity`](https://www.afterpack.dev/zh/docs/config#complexity) | 典型 gzip 后体积 | [字符串下限](https://www.afterpack.dev/zh/docs/config#strings-encode) | 适用场景 |
|---|---:|---:|:---:|---|
| `minify` | `0` | `1.09x` 至 `1.17x` | 关闭 | 不做混淆。标识符会被重命名，代码会被压缩；字符串字面量保持可读。 |
| `light` | `5` | `1.29x` 至 `1.60x` | 开启 | **默认。**在 `minify` 的基础上加入字符串编码并重写语法，不添加结构层。适合整个应用 bundle 的预设。 |
| `medium` | `7` | `6.04x` 至 `8.33x` | 开启 | 希望明显更难跟读的代码。用于整个应用 bundle 时，它会使启动时间成倍增加（[加载代价](https://www.afterpack.dev/zh/docs/presets#load-cost)），因此请先实测你的页面。 |
| `hard` | `12` | `6.62x` 至 `9.23x` | 开启 | 关键代码：定价、功能门控、许可证校验、客户端业务逻辑。用[指令](https://www.afterpack.dev/zh/docs/presets#applying-a-preset-to-one-region)把它用在这些代码上；用于整个 bundle 时，它的代价比 `medium` 更高。 |
| `extreme` | `25` | `8.25x` 至 `11.19x` | 开启 | 实际应用的最高复杂度。将它用于单个文件或[区域](https://www.afterpack.dev/zh/docs/directives#what-you-can-set)，而不是整个 bundle。 |

以上是针对同一份已压缩文件的 gzip 后输出体积，在五个真实的、已压缩的生产 bundle 上用 AfterPack 0.2.6 于 2026-10-16 测得；方法见[性能](https://www.afterpack.dev/zh/docs/performance#output-size)。

任何正数目标都会开启字符串下限。

## 加载代价

于 2026-10-24 使用 AfterPack 1.0.0 测得，测试对象是五个分别用 Vite 和 React、Vue、Astro、SvelteKit 以及 Next.js 构建的小型应用。每个应用的整个 bundle 都使用同一个预设，并与不使用 AfterPack 构建的同一应用比较；方法和每个应用的耗时见[性能](https://www.afterpack.dev/zh/docs/performance#page-load)。

| 预设 | 页面加载的 JavaScript（未压缩） | 首次有效点击的时间 |
|---|---:|---:|
| `light` | `1.12x` 至 `1.42x` | `+4 ms` 至 `+32 ms` |
| `medium` | `10.2x` 至 `11.9x` | `3.1x` 至 `11x` |
| `hard` | `14.3x` 至 `15.8x` | `3.2x` 至 `13.7x` |
| `extreme` | `27.1x` 至 `30.7x` | `5.9x` 至 `24x` |

体积在每个框架上的增长倍数大致相同。启动时间的增长取决于页面响应之前有多少这类代码要运行，因此 Next.js 应用的代价最高。框架自身的运行时也是 bundle 的一部分，每个预设都会同样保护它。

在整个应用 bundle 上，`light` 最多增加几十毫秒。从 `medium` 起，在把该预设用于整个 bundle 之前，请先实测你自己的页面。

## 默认为 `light`

不带 [`--preset`](https://www.afterpack.dev/zh/docs/config#preset) 运行 [`npx afterpack@latest dist/`](https://www.afterpack.dev/zh/docs/cli#usage)，或者安装一个[插件](https://www.afterpack.dev/zh/docs/frameworks)而不做任何配置，得到的就是 `light`。

```bash
npx afterpack@latest dist/
```

```bash
npx afterpack@latest dist/ --preset=hard    # step up
```

[命令行参数](https://www.afterpack.dev/zh/docs/config)用 `=` 连接取值。用空格分隔的取值会被当作未知参数而拒绝。

`light` 是零配置的下限。它能重写的每一个字符串字面量都会经过运行时解码器。它会重命名标识符并重写语法，且不添加结构层。从引擎 0.2.3 起，`minify` 和 `light` 不再把循环改写成回调辅助函数，从而消除了这些辅助函数在热点循环中造成的 2-5 倍减速。在 `light` 下，属性名和全局名仍然经由编码常量访问，这在非常热的代码中会有一定开销。有些字符串在任何预设下都保持可读：指令序言、`typeof` 的比较对象、运行时需要的属性名，以及导出名。[Protection Map](https://www.afterpack.dev/zh/docs/protection-map) 会列出它们，引擎也会在每次构建时报告其数量和字节总数。把预设提高到 `medium` 或以上，会在每一个词元上增加结构性工作：膨胀、控制流重塑，以及以一个共享的运行时值为密钥的字符串和整数。它不会改变字符串的可读性，因为可读性已经处于下限。

`minify` 是唯一关闭这一下限的级别。从引擎 0.2.3 起，它只重命名标识符并压缩代码，不应用任何混淆。引擎 0.2.2 在 `minify` 下还会重写语法。

## 体积预算

每份输出都带有引擎固定的运行时开销（解码器和整数表达式解释器），因此几百字节的小文件产出会比上表的比例还要大得多。请在真实的 bundle 上读取比值，而不是小文件。

[`inflation.max`](https://www.afterpack.dev/zh/docs/config#inflation-max) 默认未设置，因此复杂度目标独自决定输出体积。只有当你有一个宁可失败也不愿超出的真实体积预算时，才自己设置它。触及显式设置的值是一个错误：[`DIAG_SIZE_CAP_REACHED`](https://www.afterpack.dev/zh/docs/diagnostics#complete-reference)，没有输出，CLI 以[退出码 `3`](https://www.afterpack.dev/zh/docs/diagnostics#exit-codes)退出，插件则让构建失败。从引擎 0.2.3 起，绝不会写出保护不足的输出来替代：最终生成的文件会按上限检查，并且字符串编码总是先完成。在引擎 0.2.2 中，过紧的上限可能让字符串编码提前停止，使部分字符串保持可读。

## 直接设置目标

[`complexity`](https://www.afterpack.dev/zh/docs/config#complexity) 是一个无上限的非负数。[`--complexity=200`](https://www.afterpack.dev/zh/docs/config#complexity) 是 [Free 层级](https://www.afterpack.dev/zh/docs/tiers)下有效的覆盖值，远超 `extreme` 所设定的数值。

```bash
npx afterpack@latest dist/ --preset=hard --complexity=40
```

这相当于把 `hard` 的复杂度目标覆盖为 40；预设的其他设置都不变。向 [`--complexity`](https://www.afterpack.dev/zh/docs/config#complexity) 传入预设名称是一个错误。

## 将预设应用到单个区域

在 [Free](https://www.afterpack.dev/zh/docs/tiers) 上，一个预设覆盖整个构建：在 CLI 上用 [`--preset`](https://www.afterpack.dev/zh/docs/config#preset)，或在配置中用 `preset`。唯一更细的控制是 [`paths.exclude`](https://www.afterpack.dev/zh/docs/config#paths-exclude)，它把整个文件排除在外，并原样复制到输出中而不加保护。其余每个文件都要付出该预设的[加载代价](https://www.afterpack.dev/zh/docs/presets#load-cost)，因此除非你自己的实测结果允许更高的预设，否则请把整个应用 bundle 保持在 `light`。

在 [Pro](https://www.afterpack.dev/zh/docs/pro#what-pro-buys) 上，[源代码指令](https://www.afterpack.dev/zh/docs/directives)把更重的预设用在某个函数或文件上，bundle 的其余部分保持你的全局预设：

```js
/* @afterpack preset=extreme */
function pricingEngine(plan, region) {
  return resolve(plan, region);
}
/* @afterpack end */
```

要覆盖一个文件，就用一个指令包住它的代码。指令[只能写成块注释](https://www.afterpack.dev/zh/docs/directives#writing-one)，由[框架插件](https://www.afterpack.dev/zh/docs/frameworks)从你的源代码中读取；[CLI](https://www.afterpack.dev/zh/docs/cli) 不读取任何指令。语法和层级规则参见[指令](https://www.afterpack.dev/zh/docs/directives)。

## 下一步

- [配置](https://www.afterpack.dev/zh/docs/config)：预设涉及的每一个键。
- [CLI 参考](https://www.afterpack.dev/zh/docs/cli)：用于选择预设的参数。
- [指令](https://www.afterpack.dev/zh/docs/directives)：对单个片段使用 [`skip`](https://www.afterpack.dev/zh/docs/config#skip) 或 [`preset=<name>`](https://www.afterpack.dev/zh/docs/config#preset)。
- [AfterPack 的工作原理](https://www.afterpack.dev/zh/docs/concepts)：更高的目标面对静态阅读者究竟能带来什么。
- [最佳实践](https://www.afterpack.dev/zh/docs/best-practices)：什么该加强、什么该放过。
