Next.js 构建工具:Turbopack vs Webpack
面试题:Next.js 15 为什么默认用 Turbopack?它比 Webpack 快在哪里?
一、构建工具演进史
Next.js 构建工具时间线
─────────────────────────────────────────
2016 Next.js 1.x 纯 Webpack 时代
2018 Next.js 7+ 引入 SSR 优化
2020 Next.js 9.4 引入 Fast Refresh
2022 Next.js 12 尝试 SWC 替代 Babel(编译器)
2023 Next.js 13.4 App Router 稳定 + Turbopack dev(alpha)
2024 Next.js 15 Turbopack dev 稳定,build 仍是 Webpack
2026 Next.js 15.x Turbopack build 仍在迭代(beta)
当前状态(2026):dev 用 Turbopack 已稳定;build 仍推荐用默认 Webpack(生产打包能力更成熟)。
二、Webpack:曾经的王者
2.1 工作模式
启动时 ──→ 抓取所有入口 ──→ 解析依赖图 ──→ 全量打包 ──→ dev server 启动
↓
监听文件变化
↓
重新打包
2.2 痛点
- 冷启动慢:必须先解析全量模块构建依赖图
- HMR 退化:项目越大 HMR 越慢(与项目规模 O(n) 关系)
- JS 实现:解释型语言,单线程瓶颈明显
- 配置复杂:loader / plugin 体系庞大
三、Turbopack:Rust 编写的下一代打包器
3.1 核心定位
Turbopack 是 Vercel 出品、Rust 编写的增量打包器。Next.js 团队负责人 Tobias 曾在 Vite vs Turbopack 的争议中明确:Turbopack 定位是大型项目/全栈,Vite 定位是库/中小项目。
3.2 三个关键设计
① 增量计算(Incremental Computation)
核心创新:只重新计算真正改变的部分,任务粒度细到函数级别。
// 概念示意(不是真实代码)
struct Task {
function: fn() -> Output,
inputs: HashSet<TaskId>,
}
// 当文件 A 改变时
// 1. 标记所有依赖 A 的 Task 为 dirty
// 2. 只重跑这些 Task
// 3. 没依赖 A 的 Task 结果直接复用
类比:webpack 像每次都重新画整张画;Turbopack 像图层,只重画被修改的图层。
② 请求级编译(Request-driven)
// Vite 的策略:浏览器请求 /src/App.tsx → 编译 App.tsx
// Turbopack 的策略:浏览器请求 /src/App.tsx → 编译 App.tsx 及其所有依赖
与 Vite 看起来像,但区别在于:
- Vite 是 dev 阶段用 esbuild,build 用 Rollup
- Turbopack 是 dev/build 同一套引擎,状态可复用
③ 函数级缓存
Webpack 是文件级缓存;Turbopack 是函数级缓存。
// 假设有这样一个模块
export const getUser = (id) => /* 从 DB 查用户 */;
export const getPosts = () => /* 从 DB 查帖子 */;
// 改 getPosts 函数体(不影响 getUser)
// Webpack: 整个文件 re-compile
// Turbopack: 只 re-compile getPosts,getUser 结果直接用
3.3 性能数据(官方)
| 场景 | Webpack | Turbopack | 提升 |
|---|---|---|---|
| 大型项目 dev 启动 | 30s | 1.5s | 20x |
| HMR 速度 | 500ms | 30ms | 16x |
| 内存占用 | 1.2 GB | 400 MB | 3x |
数据来自 Vercel 2024 官方博客,基于 vercel.com 自身代码库测试。
四、Turbopack vs Vite:容易被问的对比
面试题:Turbopack 和 Vite 有什么本质区别?
| 维度 | Vite | Turbopack |
|---|---|---|
| 底层语言 | esbuild (Go) + Rollup | Rust |
| dev 模式 | 原生 ESM + 按需编译 | 增量计算 + 请求级编译 |
| build 模式 | Rollup(成熟) | Rust 自研(2026 仍在 beta) |
| 缓存粒度 | 文件级(HTTP 缓存) | 函数级(细到函数) |
| 大项目表现 | 中等(ESM 瀑布请求) | 强(Rust 内存模型 + 并行) |
| HMR 边界 | ESM 模块边界 | 函数/模块边界 |
| 框架绑定 | 框架无关(Vue/React/Svelte 都行) | 深度绑定 Next.js |
| 生态 | 插件体系成熟(rollup 兼容) | 插件体系还在补 |
核心差异一句话
Vite 解决「dev 体验」,Turbopack 解决「大型项目能 scale」。小项目 Vite 更顺手;大型 Next.js 项目 Turbopack 优势明显。
五、Turbopack 在 Next.js 中的使用
5.1 dev 模式
# 15+ 默认就是 Turbopack
next dev
# 或显式声明
next dev --turbopack
5.2 build 模式(2026 现状)
# 默认(Webpack)
next build
# 试用 Turbopack build(beta,可能有兼容问题)
next build --turbopack
生产环境 build 仍建议用 Webpack:Turbopack build 在生态兼容性上还不成熟,部分 loader/plugin 未适配。
六、SWC:被忽略的功臣
无论用 Webpack 还是 Turbopack,底层都依赖 SWC 编译 TS/JSX。
| 工具 | 语言 | 编译速度 |
|---|---|---|
| Babel | JS | 慢(解释型) |
| SWC | Rust | 快 20x |
| esbuild | Go | 快 10x |
| TSC | Go | 最慢(重在类型检查) |
面试题:Babel 慢在哪? Babel 是 AST 转换的鼻祖,但纯 JS 实现,冷启动 + 大项目都慢。SWC 用 Rust 重写了一套等价编译器,API 兼容 Babel 插件,所以能直接平替。
七、面试高频追问
Q1:为什么不用 Vite 的 esbuild 替代 Turbopack?
A:esbuild 速度快但不适合做大型项目的全量打包(HMR 边界处理、CSS 处理、SSR 构建等能力不够)。Turbopack 是为 Next.js 量身定做的,能处理 App Router 的 RSC 边界、Server Components 序列化等 Next.js 特有场景。
Q2:Turbopack 完全替代 Webpack 了吗?
A:dev 已替代;build 还在迭代。Webpack 仍是 2026 年的生产构建主力,但 1~2 年内 Turbopack build 会稳定。
Q3:HMR 的原理是什么?
A:
- Webpack:监听文件变化 → 重新编译 → WebSocket 推送 → 浏览器替换
- Turbopack:监听文件 → 标记函数级 dirty → 只重编译 dirty 函数 → 增量推送
Q4:如何排查 Next.js 构建慢?
A:
next build --profile生成 profile- 看哪个 chunk / loader 耗时
- 拆分大依赖(
manualChunks/ 动态 import) - 升级到最新 SWC