# 混淆 Vite 构建

@afterpack/vite 在 generateBundle 中混淆你的构建，早于 Vite 写出文件；它是其他所有 Vite 系包所封装的参考集成。

Source: https://www.afterpack.dev/zh/docs/frameworks/vite

`@afterpack/vite` 是参考集成。[Astro](https://www.afterpack.dev/zh/docs/frameworks/astro)、[Svelte](https://www.afterpack.dev/zh/docs/frameworks/svelte)、[SvelteKit](https://www.afterpack.dev/zh/docs/frameworks/sveltekit)、[Vue](https://www.afterpack.dev/zh/docs/frameworks/vue) 和 [Nuxt](https://www.afterpack.dev/zh/docs/frameworks/nuxt) 都封装了它，因此本页所述同样适用于这些包。

它先从你的源码中读取 [`@afterpack` 指令](https://www.afterpack.dev/zh/docs/directives)，然后在 `generateBundle` 中运行引擎，此时完整的 bundle 还在内存中，Vite 尚未写出任何文件。

## 安装

```bash
npm install -D @afterpack/vite
```

```ts
// vite.config.ts
import { defineConfig } from "vite";
import { afterpackVite } from "@afterpack/vite";

export default defineConfig({
  plugins: [afterpackVite()],
});
```

## 构建

配置到此为止。`npm run build` 现在会输出已混淆的结果；`vite dev` 不受影响，因为插件只在构建的 `generateBundle` 阶段起作用。Vite 写出的是已混淆的 chunk，原始文件从不落盘；混淆失败时，`outDir` 保持原样。

它混淆的是 Vite 构建出来的内容：输出 bundle 中的每一个 chunk。Vite 从 `publicDir` 原样复制到输出目录的文件没有经过构建，不在 bundle 中，也不会被处理。任何需要保护的内容都应经过打包工具。

插件会写出：

- [`protectionMap.html`](https://www.afterpack.dev/zh/docs/protection-map)：整个构建的一份合并地图，始终写入项目根目录下已被 gitignore 的 `.afterpack/`。它内嵌了你的原始源码。绝不要让它落入对外提供服务的目录。
- `foo.js.map`：启用 map 时写出的 [source map](https://www.afterpack.dev/zh/docs/config#sourceMap-enabled)。关闭 map 时（这是生产环境的默认值），Vite 自身的 map 条目会被丢弃，而不会写在已混淆的 chunk 旁边。

这里不会有 `foo.backup.<hash>.js`。已混淆的字节直接回写到 bundle 中，没有已写出的文件可供备份放在旁边，因此 [`build.backup: true`](https://www.afterpack.dev/zh/docs/config#build-backup) 只会发出警告，而不会写出备份。

`.afterpack/` 自带一个 `.gitignore`，因此插件从不修改你的 `.gitignore`。产物落入对外提供服务的路径时，它会发出警告。

## 验证

[Protection Map](https://www.afterpack.dev/zh/docs/protection-map) 默认会在 Vite 输出 source map 且构建不在 CI 中（`CI=true` 或 `CI=1`）时写出；[生产构建](https://www.afterpack.dev/zh/docs/config#build-mode)不会关闭它。除非设置了 `build.sourcemap`，否则 Vite 不会输出 source map，因此在常规构建中 Protection Map 默认关闭。想要查看时请显式开启：

```ts
afterpackVite({ protectionMap: { enabled: true } });
```

在未设置 `build.sourcemap` 的情况下，这同时会开启 Vite 自身的 source map，以便地图能够渲染你的原始源码。找不到可用的 map 时，插件没有可据以渲染原始源码的来源，并会如实说明。

报告写入 `.afterpack/protectionMap.html`。它包含你的完整源码。绝不要提交或对外提供它，只在本地查看。

*(live Protection Map demo embed — see https://www.afterpack.dev/protection-map-demo)*

> **绝不要把 source map 发布到生产环境**
>
> 一个 `.map` 文件就能直达你的原始源码。绝不要部署它。

## 选项

每个[配置键](https://www.afterpack.dev/zh/docs/config)都可以通过这里的选项对象传入，例如 `afterpackVite({ preset: "hard", complexity: 40 })`。所有选项均为可选。具名导出为 `afterpackVite`。

`preset` 和 `complexity` 设置的是不同的东西。[预设](https://www.afterpack.dev/zh/docs/presets)是一组打包好的设置：它同时设定复杂度目标、总输出体积倍数和[膨胀预算](https://www.afterpack.dev/zh/docs/config#inflation-max)。`complexity` 则是这组设置中的一个数字：

```ts
afterpackVite({ preset: "hard", complexity: 40 }); // hard's size limits, target 40
```

## 指令

插件会在任何压缩器剥离注释之前，从你的源码中读取指令。[语法](https://www.afterpack.dev/zh/docs/directives#writing-one)只接受块注释：

```ts
/* @afterpack preset=hard */
function verifyLicense(token) {
  // ...
}
/* @afterpack end */
```

`// @afterpack …` 这样的行注释不会被扫描，并会作为[诊断信息](https://www.afterpack.dev/zh/docs/diagnostics)报告出来。

指令可以设置的每个键都列在[指令](https://www.afterpack.dev/zh/docs/directives#what-you-can-set)页面上。

捕获时会跳过 `node_modules` 以及非 JS/TS 文件。写在 `.vue` 或 `.svelte` 单文件组件内的指令不会被捕获。请改放到该组件导入的普通 `.ts`/`.js` 模块中。设置 [`directives.enabled: false`](https://www.afterpack.dev/zh/docs/config#directives-enabled) 可完全跳过扫描。

## 边界情况

### 库模式

`build.lib` 的输出是一份公开契约。AfterPack 作用于最终的 bundle，因此导出名称会保留；内部实现仍会被重命名和重组。

### 多个输出

每次 Vite 构建只混淆自己的 bundle，不涉及其他内容，因此即使多次构建写入同一个目录，它们也是各自独立的处理。如果它们共享运行时契约，请为它们固定同一个 [`seed`](https://www.afterpack.dev/zh/docs/config#seed)。

### 内容哈希文件名

每个 chunk 的文件名都[跟随其混淆后的字节](https://www.afterpack.dev/zh/docs/builds#file-names-and-caching)。一个以普通资源而不是 Vite 构建的 chunk 形式进入 bundle 的 JavaScript 文件，会保留 Vite 根据其未混淆字节给出的文件名，构建会发出警告：请把它作为模块导入，或在不使用长期缓存的情况下提供它。bundle 中预压缩的 `.gz` 和 `.br` 副本会根据混淆后的字节重新生成，其他格式的预压缩副本会让构建失败。

### 子资源完整性

计算子资源完整性哈希的 Vite 插件会被拒绝，因为它的 `integrity` 属性将与混淆后的文件不匹配。请移除它，并在构建之后根据部署的文件计算完整性。

## 下一步

- [框架](https://www.afterpack.dev/zh/docs/frameworks)：完整矩阵。
- [构建与 CI](https://www.afterpack.dev/zh/docs/builds)：种子、开发与生产环境的区别、原样提升同一份字节。
- [Protection Map](https://www.afterpack.dev/zh/docs/protection-map)：如何阅读报告。
- [配置](https://www.afterpack.dev/zh/docs/config)：引擎的配置面。
- [指令](https://www.afterpack.dev/zh/docs/directives)：每个标记键及其作用。
- [预设](https://www.afterpack.dev/zh/docs/presets)：五级阶梯。
