TL;DR
如果今天让我新开一个前端项目,我大概率会优先考虑这套组合:
- Vite 8 作为 dev/build 的基础
- Vite+ 作为统一入口,接管 create、dev、build、test、lint、format、run
- Oxlint + Oxfmt 替代大部分 ESLint / Prettier 的日常反馈链路
- Vitest 4 做单测、组件测试和 Browser Mode
- Playwright 负责真正的端到端和移动端模拟
- monorepo 场景下,用 Vite+ 的 task runner 和缓存先顶上,而不是一上来就引入很重的工程编排
我现在越来越不想在新项目里重新拼一遍工具链了。
不是说 ESLint、Prettier、pnpm workspace、Turborepo、Vitest、Playwright 这些东西不好。它们都很好,也都很成熟。但问题是,每次新项目都要重新想一遍:
- lint 怎么配
- format 怎么配
- test 怎么拆
- monorepo 脚本怎么跑
- CI 缓存怎么写
- 单仓库升级到 monorepo 要不要重构命令
- Node 版本和包管理器怎么约束
这些事情单独看都不大,但加起来就是一种很稳定的消耗。
所以 Vite+ 让我感兴趣的地方,不是它又多了一个 CLI,而是它想把这些原本分散的前端基础设施,收束到一个入口里。
说白了,我想要的不是“更酷的脚手架”,而是一个新项目可以直接沿用的默认工作流。
1. 为什么是现在:Vite 8 之后,前端工具链的底座变了
Vite 8 最大的变化,是把底层 bundler 收束到了 Rolldown。
以前 Vite 的经典结构是:
- 开发时主要依赖 esbuild 的速度
- 生产构建走 Rollup 的生态和稳定性
这个设计很务实,也撑起了 Vite 很长时间。但它也带来一个长期存在的问题:开发和生产不是同一个 bundler,很多行为要靠适配和对齐。
到了 Vite 8,Rolldown 成为统一的 Rust bundler。官方说构建相对 Rollup 有 10-30x 的提升,同时继续维持 Rollup 插件兼容。这件事的意义不只是“构建更快”,而是 Vite、Rolldown、Oxc 这几层开始真正靠近了。
我觉得这里有一个很关键的趋势:
前端工具链正在从“很多 JS 工具拼起来”,慢慢变成“一组底层能力协同工作”。
过去我们习惯了这种组合:
- Babel / SWC 负责转换
- ESLint 负责检查
- Prettier 负责格式化
- Rollup / webpack / esbuild 负责打包
- Vitest / Jest 负责测试
- 任务编排再交给 npm scripts、Turborepo 或 Nx
它当然能跑,但每一层之间都有边界成本。配置重复、解析重复、文件扫描重复、缓存策略也重复。
Vite 8 + Vite+ 的方向,是把这些东西尽量统一到一套更贴近底层的链路里。
这也是我觉得它值得单独写一篇的原因。
2. Vite+ 到底解决什么问题
Vite+ 不是简单的 create-vite 加强版。
VoidZero 对它的定位是 unified toolchain,也就是一个统一的 Web 开发入口。它把这些工具组合到一起:
- Vite
- Vitest
- Oxlint
- Oxfmt
- Rolldown
- tsdown
- Vite Task
对应到命令上,大概是:
vp create # 创建项目 / monorepovp install # 自动使用正确包管理器安装依赖vp dev # 启动开发服务vp check # lint + format check + type checkvp test # 运行 Vitestvp build # 生产构建vp run # 跑脚本和任务,支持 monorepo 依赖顺序与缓存vp pack # 打包库或独立应用vp env # 管理 Node 环境我比较喜欢的是它的目标很直接:
把“项目该怎么跑”这件事从 package.json 脚本里抽出来,变成更稳定的工程约定。
很多前端项目刚开始都很简单:
{ "scripts": { "dev": "vite", "build": "vite build", "test": "vitest", "lint": "eslint .", "format": "prettier --write ." }}但项目长大后,脚本会慢慢变成这样:
{ "scripts": { "dev": "xxx", "dev:web": "xxx", "dev:admin": "xxx", "build": "xxx", "build:web": "xxx", "build:admin": "xxx", "lint": "xxx", "lint:fix": "xxx", "format": "xxx", "format:check": "xxx", "typecheck": "xxx", "test": "xxx", "test:unit": "xxx", "test:browser": "xxx", "test:e2e": "xxx" }}然后每个项目都不一样。
新同事进来第一件事不是写业务,而是先问:这个仓库到底该跑哪个命令?CI 和本地是不是同一套?monorepo 里改了一个 package,要不要全量 build?
Vite+ 想统一的就是这些日常动作。
这件事并不性感,但很有价值。
3. Oxlint + Oxfmt:先把反馈速度提上来
我一直觉得 lint 和 format 的价值不在“显得工程规范”,而在反馈速度。
如果一个工具慢到大家不愿意本地跑,那它在团队里就会慢慢变成 CI 惩罚器。代码写完了,push 上去,CI 红了,然后再回来改。这个流程非常消耗人。
Oxlint 的定位很明确:不用配置也能抓很多错误和无用代码,并且速度比 ESLint 快很多。Oxc 文档里提到,Oxlint 大概是 ESLint 的 50-100 倍速度,并且会随着 CPU 核心数扩展。
Oxfmt 则是 Prettier-compatible formatter。它支持 JS、JSX、TS、TSX、JSON、YAML、HTML、Vue、CSS、Markdown、MDX 等常见格式。官方说 Oxfmt 大概比 Prettier 快 30 倍,并且 JavaScript / TypeScript 已经通过 Prettier 的 conformance tests。
当然,这里我不会直接说“可以无脑替代 ESLint 和 Prettier”。
更稳的策略应该是分阶段:
小到中型新项目
可以直接尝试:
vp check把 lint、format check、type check 都收进一个命令里。
如果不用 Vite+,也可以单独这样配:
{ "scripts": { "lint": "oxlint", "format": "oxfmt", "format:check": "oxfmt --check" }}已有大型项目
不要一把梭。
我会先这样做:
- 保留现有 ESLint
- 先在本地和 CI 前置跑 Oxlint
- 用
eslint-plugin-oxlint关闭 Oxlint 已经覆盖的 ESLint 规则 - 对必须依赖生态插件的规则,继续交给 ESLint
- 等差异足够小,再考虑进一步迁移
这和 Oxc 官方建议也比较接近:大型项目可以先让 Oxlint 在 ESLint 前面跑,拿到更快的反馈回路。
我的判断是:
Oxlint 更适合做第一层快速反馈,ESLint 暂时还适合兜底复杂生态规则。
比如某些框架插件、公司内部规则、非常细的可访问性策略,短期内不一定都能丢给 Oxlint。
但新项目就不一样了。新项目没有历史包袱,我更愿意直接让 Oxlint/Oxfmt 成为默认项。
4. 单代码仓库:先简单,但不要把未来堵死
如果只是一个普通前端应用,我现在会尽量保持结构简单:
my-app/ src/ tests/ vite.config.ts vitest.config.ts package.json默认命令大概是:
vp devvp checkvp testvp build这里关键不是命令少,而是命令语义稳定。
我希望以后不管项目变成什么样,大家都知道:
- 开发就是
vp dev - 提交前就是
vp check - 测试就是
vp test - 构建就是
vp build
这比在 package.json 里写十几个类似命令更容易维护。
我会把配置控制在很少的范围内:
import { defineConfig } from 'vite'
export default defineConfig({ // 先保持少配置})新项目最怕一开始就工程化过度。
有些东西可以晚点再引入:
- 多应用
- 多 package
- 复杂缓存
- 代码生成
- e2e 矩阵
- 内部组件库
但有些约定最好一开始就定好:
- Node 版本
- 包管理器
- lint / format / typecheck 入口
- test 文件命名
- CI 必跑命令
这就是 Vite+ 这种统一入口的价值:它不会强迫你一开始就做成 monorepo,但它给后面扩展留下了路径。
5. monorepo:不要为了 monorepo 而 monorepo
我对 monorepo 的态度一直比较保守。
不是不能用,而是要先问:你真的需要吗?
这些情况我觉得可以上:
- 一个产品里有多个前端应用,比如 web、admin、docs
- 有共享组件库、工具库、类型包
- 多个 package 需要一起改、一起测、一起发
- 团队已经被跨仓库同步折磨过了
如果只是一个应用,硬拆成 monorepo,通常只是把简单问题复杂化。
一个比较舒服的结构可能是这样:
repo/ apps/ web/ admin/ packages/ ui/ config/ shared/ vite.config.ts package.jsonVite+ 里比较值得关注的是 vp run 和 Vite Task。
它不像普通 pnpm run 只是把命令跑起来,而是会考虑:
- workspace 感知
- 任务依赖顺序
- 自动缓存
- 哪些输入文件会影响任务结果
- 哪些环境变量会影响缓存
比如在 monorepo 里,根配置可以打开缓存:
import { defineConfig } from 'vite'
export default defineConfig({ run: { cache: true, },})然后日常就可以:
vp run buildvp run testvp run lint当然,缓存不是魔法。
我会特别注意这几件事:
- 生成物不要被算进输入里,比如
dist/ - 临时文件不要干扰缓存,比如
.vite-temp/ - 任务依赖要写清楚,尤其是
packages/ui被apps/web使用时 - 影响构建的环境变量要纳入缓存 key
- CI 和本地的缓存策略不要完全割裂
我比较喜欢的实践是:
monorepo 先用 Vite+ 的任务能力跑起来,只有当它不够用时,再考虑引入更重的 Nx / Turborepo。
这不是说 Nx 或 Turborepo 不好,而是新项目不一定需要一开始就背这么多概念。
6. Vitest:从单测到 Browser Mode
Vitest 现在已经不只是“Vite 生态里的 Jest 替代品”了。
对我来说,它比较适合承担三层测试:
- Node 环境单测:工具函数、数据转换、业务逻辑
- jsdom / happy-dom 组件测试:轻量 DOM 行为
- Browser Mode:需要真实浏览器能力的组件或交互测试
Vitest 的 projects 配置很适合把这些拆开。以前叫 workspace,现在官方更推荐 projects。
比如可以这样:
import { defineConfig } from 'vitest/config'import { playwright } from '@vitest/browser-playwright'
export default defineConfig({ test: { projects: [ { test: { name: 'unit', environment: 'node', include: ['tests/unit/**/*.test.ts', 'src/**/*.unit.test.ts'], }, }, { test: { name: 'browser', include: ['tests/browser/**/*.test.ts', 'src/**/*.browser.test.ts'], browser: { enabled: true, provider: playwright(), instances: [{ browser: 'chromium' }], }, }, }, ], },})这样做的好处是边界很清楚:
- 纯逻辑不要进浏览器,快一点
- 需要真实 DOM / layout / browser API 的测试再进 Browser Mode
- 不同测试环境不要互相污染
我现在不太喜欢所有测试都塞进一个环境里。
因为最后一定会变成这种局面:有的测试明明只需要 Node,却被迫跑在更重的环境里;有的测试明明依赖真实浏览器,却在模拟环境里靠 mock 撑着。
拆开之后,心智负担反而更低。
7. 移动端:不要只改 viewport
如果项目要认真考虑移动端,我不会只写几个响应式断点就结束。
移动端至少要看这些东西:
- 视口尺寸
- touch 行为
- user agent
- safe area
- 横竖屏
- 输入法弹起
- 滚动容器
- hover 不存在时的交互
- 弱网络和慢设备下的体验
Vitest Browser Mode 可以覆盖一部分真实浏览器里的组件行为,但真正的移动端端到端,我还是会交给 Playwright。
Playwright 的 device emulation 可以模拟很多设备参数,比如 userAgent、screenSize、viewport、hasTouch、isMobile、locale、timezone 等。
例如:
import { defineConfig, devices } from '@playwright/test'
export default defineConfig({ projects: [ { name: 'Desktop Chrome', use: { ...devices['Desktop Chrome'] }, }, { name: 'Mobile Safari', use: { ...devices['iPhone 15'] }, }, { name: 'Pixel', use: { ...devices['Pixel 7'] }, }, ],})但这里也要清醒一点:模拟不等于真机。
它可以很好地发现:
- 响应式布局错位
- touch 点击区域太小
- 移动端菜单打不开
- Safari/WebKit 下的兼容问题
- 表单在小屏幕上不可用
但它不一定能完全覆盖:
- 真机性能
- 某些 WebView 行为
- 软键盘遮挡
- 系统级手势冲突
- 真实网络和机型差异
所以我的默认策略会是:
- 日常 CI:Playwright 移动端模拟
- 关键版本:至少拿一台 iPhone 和一台 Android 真机过主流程
- PWA / H5 活动页:额外关注 WebView 和分享入口
移动端最怕的是桌面看起来没问题,真到手机上一堆细节炸掉。
8. 我会怎么给新项目定默认脚本
如果用 Vite+,我会尽量减少自定义脚本,让团队形成固定动作。
{ "scripts": { "dev": "vp dev", "check": "vp check", "test": "vp test", "build": "vp build" }}如果是 monorepo:
{ "scripts": { "dev": "vp dev", "check": "vp check", "test": "vp run test", "build": "vp run build" }}CI 里大概保持这样:
vp installvp checkvp testvp build如果有 e2e:
pnpm playwright install --with-depspnpm playwright test当然,这里要看 Vite+ 后续和 Playwright 的集成情况。如果后面它能把 e2e 也更自然地收进 vp test,那会更舒服。
9. 风险:Vite+ 还在 Alpha,不要把它当成完全成熟的基建
这篇虽然是在推荐 Vite+,但我不想把它写成无脑吹。
Vite+ 现在还处在 Alpha 阶段,这意味着:
- API 和配置可能变化
- 某些边界场景还没被大量生产项目验证
- monorepo 缓存可能会遇到细节问题
- 生态插件不一定全部适配到最佳状态
- 团队成员需要接受新的命令入口
所以我的建议会分场景:
| 场景 | 我的建议 |
|---|---|
| 个人项目 / 新项目 | 可以直接试 Vite+ |
| 小团队新项目 | 可以试,但保留回退方案 |
| 中大型已有项目 | 先迁 Oxlint/Oxfmt 或 Vite 8,不急着全量 Vite+ |
| 严肃生产 monorepo | 先开分支验证缓存、CI、构建差异 |
| 强依赖复杂 ESLint 插件 | 暂时保留 ESLint 兜底 |
我的态度是:
新项目可以激进一点,老项目要保守一点。
工具链迁移最怕的是为了速度,把确定性弄丢了。
10. 我现在的默认选择
如果是我来做一个新的前端项目,我大概会这样选:
普通 Web App
- Vite 8
- Vite+
- Oxlint + Oxfmt
- Vitest unit tests
- Playwright e2e
组件库 / 工具库
- Vite+ / tsdown
- Vitest
- Oxlint + Oxfmt
- examples 用 Vite 跑起来
- 发布前跑
vp pack
中后台 monorepo
- apps/web
- apps/admin
- packages/ui
- packages/shared
- Vite+ task cache
- Vitest projects
- Playwright 跑核心流程
移动端优先 H5
- Vite+
- Vitest Browser Mode
- Playwright mobile projects
- 真机主流程检查
- 对 safe area、touch、输入法、滚动做额外 checklist
最后
我觉得 Vite+ 最有价值的地方,不是让某一个命令快一点。
真正重要的是,它试图把前端项目里那些每天都会发生、但每个仓库都写得不一样的事情统一起来:开发、构建、测试、检查、格式化、任务运行、monorepo 缓存、运行时管理。
这听起来没有那么酷,但对长期维护很重要。
前端工具链这些年一直在变快。以前我们关心的是“这个工具比那个工具快多少”,现在我更关心的是另一件事:
一个项目从第一天到变复杂之后,工具链还能不能保持同一套心智模型。
如果 Vite+ 后面稳定下来,它很可能会成为我新前端项目的默认入口。
至少现在,我愿意把它放进首选列表里。
参考资料: