Vite+ 第二章:拆解 Vite+ 内部到底装了什么
译注:本文翻译自 dev.to 前端专栏作者 Othmane Nemli 的系列文章《Vite+ — Chapter 2: What's Inside Vite+?》,原文发布于 2026 年 9 月 25 日。原文链接见文末。本文为系列第二章,第一章讨论了「为什么还需要另一个 JavaScript 工具」。
在第一章中,我们探讨了 Vite+ 存在的理由。简单来说:现代 JavaScript 项目会用到许多优秀的工具,但把这些工具连接起来并长期维护,往往会变得相当复杂。Vite+ 试图将其中若干工具整合到同一套工作流之下。
但这又引出了另一个问题:Vite+ 内部究竟装了什么?
当你看到下面这些命令时:
vp dev
vp test
vp check
vp build它们背后到底发生了什么?让我们打开这个工具箱看看。
Vite+ 不是一个大而全的工具
首先要理解的是,Vite+ 并不是要取代一切。它把多个各司其职的工具聚合在一起。简化后的结构大致如下:
Vite+
|
+--------------+--------------+
| | |
Development Quality Testing
| | |
Vite Oxlint / Oxfmt Vitest
|
Building
|
Rolldown
+-----------------------------+
|
Libraries
|
tsdown
+-----------------------------+
|
Tasks
|
Vite Task每个工具解决不同的问题,关键在于 Vite+ 提供了一致的方式来使用它们。下面逐一介绍。
1. Vite —— 开发
先从大多数开发者已经熟悉的工具说起。Vite 主要是一个开发服务器和构建工具。
假设你在构建一个 React 应用,目录结构可能是:
src/
├── App.tsx
├── main.tsx
└── components/
└── Button.tsx你希望写完代码后立刻在浏览器中看到结果,这正是 Vite 的用武之地。你可以用 vp dev 启动开发服务器,底层实际工作的是 Vite。
Vite 为什么有用?
想象你把一个 Button 组件从返回 Hello 改成返回 Hello World。你显然不希望每改一行就停掉服务器、重新构建整个应用、再重启浏览器。Vite 通过 热模块替换(HMR) 等特性提供快速的开发体验。简单来说:
你修改代码
↓
Vite 感知到变化
↓
浏览器更新
↓
你继续工作这就是 Vite 成为现代前端开发常见组成部分的原因。在 Vite+ 中,你依然得到 Vite,它并没有被替换。
2. Vitest —— 测试
写代码只是一半工作,我们还需要确认它能正常运行,这就是 Vitest 的职责。
假设有这样一个函数:
function add(a: number, b: number) {
return a + b;
}你可以写一个测试:
import { expect, test } from "vitest";
test("adds two numbers", () => {
expect(add(2, 3)).toBe(5);
});然后运行 vp test。Vite+ 使用 Vitest 作为测试工具。
为什么选 Vitest?
你可能已经知道 Jest 之类的工具。这里的关键并不是评判哪个测试运行器「最好」,而在于 Vitest 与 Vite 生态配合得非常自然。这意味着你的开发环境和测试环境可以共享大量配置和行为。概念上:
Development
↓
Vite
Testing
↓
Vitest
↓
Shared ecosystem这种集成正是 Vitest 能自然融入 Vite+ 的原因之一。
3. Rolldown —— 打包
接下来是一个听起来有点吓人的词:打包器(bundler)。别担心,概念其实很简单。
你的应用可能包含成百上千个文件:
src/
├── main.ts
├── App.tsx
├── components/
│ ├── Button.tsx
│ ├── Modal.tsx
│ └── Header.tsx
├── utils/
│ ├── date.ts
│ └── format.ts
└── ...浏览器并不一定需要原封不动地接收所有这些文件。打包器会分析文件之间的关系,并为生产环境生成优化后的产物。概念上:
你的源代码
↓
打包器
↓
生产环境文件
↓
浏览器传统上,Vite 使用 Rollup 进行生产构建。Vite+ 则引入了 Rolldown——一个用 Rust 编写的新一代打包器,旨在为 Vite 生态提供高性能的打包基础。你未必需要直接与 Rolldown 打交道,只需运行 vp build,让工具链处理即可。
打包器为什么重要?
想象你的应用有 10000 行源代码。你不希望手动去判断「哪些文件应该被包含」「哪些模块相互依赖」「这些文件能否合并」「无用代码能否移除」。打包器会构建依赖图:
App
├── Header
├── Dashboard
│ ├── Chart
│ └── Table
└── Utils然后生成优化后的输出。这正是构建性能变得重要的领域之一,对大型项目尤其如此。
4. tsdown —— 构建库
开发者构建的不只是应用,有时也在创建库,比如 my-ui-library、my-auth-library、my-api-client、my-utils。
假设你导出了一个函数:
export function formatDate(date: Date) {
// ...
}你希望其他开发者能通过 npm install my-utils 安装它。此时构建流程的需求就不同了,你可能需要:JavaScript 产物、TypeScript 类型声明、不同的模块格式、包元数据、优化输出。
这正是 tsdown 进入 Vite+ 生态的位置。你可以用 vp pack 来打包一个库。关键区别在于:
Application
↓
vp build
Library
↓
vp pack这两种工作流的目标并不相同。
5. Oxlint —— 代码检查
现在谈谈代码质量。想象有人写下这样的代码:
const user = getUser();
console.log(user);
if (user) {
// ...
}代码也许能跑,但可能存在问题:未使用的变量、可疑的模式、意外的 bug、不一致的代码、团队不允许的做法。Linter 就是用来查找这类问题的。
Vite+ 使用 Oxlint 进行代码检查。可以把它理解为:
你的代码
↓
Oxlint
↓
潜在问题例如 vp check 可以把代码检查纳入整体项目检查之中。
为什么还要另一个 linter?
你可能会想:「可我们已经有 ESLint 了。」没错,ESLint 仍被广泛使用,生态庞大。Oxlint 则采取了不同的思路,重点放在性能上。它属于更广泛的 Oxc 工具链,同样用 Rust 编写。
对 Vite+ 而言,重要的理念不是「ESLint 不好」,而是:「如果常见的开发工具能极其快速,并整合进同一条工具链,会怎样?」这正是选择 Oxlint 这类工具背后的哲学。
6. Oxfmt —— 格式化
代码检查和格式化相关,但不是一回事。Linter 问的是:「这段代码有没有潜在问题?」格式化工具问的是:「能否让代码遵循一致的风格?」
例如下面两段在功能上相似:
const user={name:"John"};和:
const user = {
name: "John",
};但大多数团队希望所有人使用相同的格式规则,这就是 Oxfmt 的职责。可以把它看作工作流中的格式化环节:
源代码
↓
Oxfmt
↓
一致的格式这同样类似于开发者传统上使用 Prettier 的场景。
代码检查 vs 格式化
如果你刚接触前端开发,这个区别值得记住。
格式化:「让代码看起来一致。」例如 const name="John" 会变成 const name = "John";。
代码检查:「查找可能有问题的模式。」例如 const unusedVariable = 123;,linter 会提示 unusedVariable is never used。
所以:
Oxfmt → 代码长什么样
Oxlint → 代码中的潜在问题两者都可以纳入 vp check。
7. Vite Task —— 运行任务
随着项目增长,这部分会变得更有意思。大多数项目都有任务,例如 build、test、lint、typecheck。你可能会在 package.json 中定义它们:
{
"scripts": {
"build": "...",
"test": "...",
"lint": "..."
}
}对小型项目来说这足够了。但想象一个 monorepo:
apps/
web/
admin/
packages/
ui/
auth/
utils/此时任务之间产生了关系。例如:
ui
↓
web如果 UI 包发生变化,Web 应用可能需要重新构建。这正是任务运行器发挥作用的地方。Vite+ 包含 Vite Task,用于任务执行与缓存。
任务依赖
具体来说,假设 packages/ui 被 apps/web 使用。你修改了 packages/ui/Button.tsx,依赖图是:
ui
↓
web一个聪明的任务运行器能理解:「Web 应用依赖 UI,所以 Web 构建可能需要运行。」但假设另一个包 packages/utils 没有变化,如果那里没有相关改动,重建所有内容可能就是多余的。这正是任务图和缓存发挥作用的地方。
缓存
缓存听起来复杂,但基本思路很简单。假设你运行 vp run build,构建耗时 30 秒。你在没有任何改动的情况下再次运行,为什么要再花 30 秒做完全相同的工作?缓存可以记住上一次的结果。概念上:
第一次运行:
Source
↓
Build
↓
Result
↓
Cache然后:
第二次运行:
Source
↓
相关内容有变化吗?
↓
没有
↓
复用结果这在大型仓库和 CI 中尤其有价值。我们会在第四章更深入地讨论这一点。
8. 运行时与包管理
还有一部分开发体验容易被忽视:环境本身。
一个项目可能期望 Node.js 22 + pnpm,而另一个项目期望 Node.js 20 + npm。Vite+ 也提供了管理运行时和包管理器环境的命令与工作流。例如 vp env 可以作为该工作流的一部分使用。
目标是让项目所使用的环境更加明确、可复现。这很重要,因为「在我机器上能跑」是软件开发中最古老的问题之一。
把各部分拼在一起
现在我们可以看清简单的 vp 命令背后是什么了:
Vite+
|
+------------------+------------------+
| | |
Development Quality Testing
| | |
Vite Oxlint + Oxfmt Vitest
|
Building
|
Rolldown
|
Libraries
|
tsdown
|
Tasks
|
Vite Task你不必在第一天就分别学习每一个工具,而是可以通过一套通用工作流与它们交互。例如:
vp dev→ 开发vp test→ 测试vp check→ 代码质量vp build→ 构建
原文链接
https://dev.to/othmane_nemli/vite-chapter-2-whats-inside-vite-1n2k