装备章从发动机讲起。本节回答四个问题:Vite 快在哪、Webpack 什么时候还值得用、Nuxt 默认怎么切代码、体积大了怎么查。构建侧的优化是第 8 章运行时优化的前置——很多"性能问题"其实是"打包问题"。
Vite 是 Nuxt 3 起的默认引擎,快的原因在开发模式:不打包,按需编译。开发服务器启动时不做全量构建,浏览器请求某个模块时才编译那个模块,依赖用原生 ESM 按需供给——项目越大,这种"用到才处理"的启动优势越明显。第 1 章那个"几秒启动"的体验就来自这里。
Webpack 路线的存量价值在兼容:老项目的特殊 loader、某些闭源插件、特定团队的深度定制。Nuxt 保留切换能力:
// nuxt.config.ts:切回 Webpack 构建(bundler 模块) export default defineNuxtConfig({ builder: 'webpack', // 默认 'vite' })
| 维度 | Vite | Webpack |
|---|---|---|
| 开发启动 | 按需编译,秒级 | 全量打包,随体积变慢 |
| HMR 热更 | 模块级精确失效 | 依赖图级联更新 |
| 生态 | 主流新工具默认支持 | 老loader与深度定制存量 |
| 选择建议 | 新项目无脑选 | 仅有历史包袱时评估切换成本 |
生产构建两者殊途同归:都做打包、压缩、指纹化。日常说的"构建优化"主要指生产侧的体积与加载结构。

默认一:自动代码分割。每个页面一个分片,访问时按需加载(首屏分片随 HTML 的模块预加载声明直达,客户端导航走 5.3 的预取)。你什么都不配置就有这个结构——但结构好不等于体积好。
默认二:摇树(Tree-shaking)。生产构建剔除未使用的导出。它的生效有前提:依赖得是 ESM、副作用声明正确。CJS 老包或乱写 sideEffects 的包摇不动,只能整包进 bundle。
抓手一:Lazy 组件。首屏用不到的重组件加 Lazy 前缀按需加载:
<template> <ProductGrid :items="items" /> <!-- 弹窗里的富文本编辑器:不点开就不下载 --> <LazyRichEditor v-if="showEditor" v-model="content" /> </template>
抓手二:体积分析。优化前先测量,别凭感觉:
# 构建并生成产物分析报告 NITRO_ANALYZE=true npm run build
命令会启动一个本地报告页面,矩形面积即模块体积。典型的意外大户:完整引入的图标库(该按需)、日期库全量 locale(该按 locale 引)、被三行工具函数拖进来的巨型依赖(该自写)。找到大户后的动作优先级:先换按需引入,再考虑替换依赖,最后才是手动分包。
对确实大的依赖(图表、编辑器、地图),手动独立成块提升缓存效率——业务代码天天变,这类库很少变,分开放能吃到长期缓存:
// nuxt.config.ts:Vite 侧手动分包示例 export default defineNuxtConfig({ vite: { build: { rollupOptions: { output: { manualChunks: { 'chart-vendor': ['chart.js'], }, }, }, }, }, })
资源提示(preload/prefetch)Nuxt 已按最佳实践默认注入(入口分片 preload、预取链路对应 5.3)。需要干预时在 nitro 或 app.head 层面调整,但先测量后调整——默认策略多数场景已是优解。
上线前花五分钟巡检产物,能拦住一批"本地好好的"事故。在 .output/public 里核对四件事:
其一,入口体积。首屏入口 JS(带 entry 字样的分片)控制在三百 KB 级以内是健康线,超过就要回体积分析找大户。其二,分片数量。页面分片几十个属正常(按需加载的代价),但首屏 HTML 里 preload 的脚本清单过长说明共享块抽取出了问题。其三,source map 是否带上。生产默认不带完整 map(安全且省体积),需要线上排错时用隐藏 map 方案(9.2 提及)。其四,静态资源指纹。文件名应带内容哈希——没带说明缓存策略退化,用户更新拿到的可能是旧资源。
巡检发现问题的回溯路径都是 7-1 图里的结构:入口过大查共享层与首屏组件,分片异常查页面依赖。构建配置的改动(分包、压缩、别名)每改一处跑一次巡检,防止"优化"引入新问题。
理解构建全貌还差一块拼图:开发时的模块与产物里的分片是怎么对应的。Vite 的处理分两阶段——开发态用原生 ESM 直接服务源文件(浏览器按 import 图按需拉取,这就是秒级启动的原理),生产态用打包器做完整分析(分割、摇树、压缩、指纹化)。两阶段的行为差异解释了一批"开发正常、构建异常"的现象:开发态不摇树所以多余导出无感,生产摇树后副作用声明不规范的依赖会炸;开发态按需编译掩盖了循环依赖,生产打包时序变化问题显形。遇到"dev 好的 build 崩",优先查依赖的副作用声明与循环 import。
⚠️ 常见坑:为了"优化"把所有组件都 Lazy 化。首屏内的组件 Lazy 化反而增加请求瀑布(等 JS 到了才发现要再下载组件)。判据:视口外、交互后才出现的组件才值得 Lazy。
💡 关键直觉:构建优化的对象是"每个用户要下载的字节",运行时优化(第 8 章)的对象是"字节到位后的渲染速度"。先砍字节再谈渲染,顺序反了事倍功半。