5月31日 16:17

Vite 依赖预构建有什么用?什么时候需要手动配置?

Vite 启动快,并不是因为它什么都不做。业务源码交给浏览器按需请求,但 npm 依赖会先做一次依赖预构建。它把 CommonJS/UMD 转成 ESM,也把零散依赖合并成较少请求。

为什么需要预构建

浏览器原生 ESM 不认识裸模块导入,也不能直接执行 CommonJS。Vite 会扫描入口源码,找到第三方依赖,再用 esbuild 打到缓存目录。

ts
import React from 'react' import { debounce } from 'lodash-es'

如果让浏览器逐个请求某些 ESM 包内部的几百个文件,开发首屏会很慢。预构建的取舍是冷启动多做一点工作,换来后续加载更稳定。

缓存在哪里

预构建结果通常在 node_modules/.vite。Vite 会根据 lockfile、package.json、配置文件判断缓存能否复用。依赖没变时,第二次启动通常更快。

bash
npx vite --force

--force 会强制刷新预构建缓存。升级依赖、切分支或调整别名后,如果开发环境行为很怪,先刷新缓存是有效排查手段。不要把 .vite 提交到仓库,它只是本机开发产物。

include 用来补扫描盲区

Vite 的扫描很快,但不是全知全能。动态导入、运行时拼接、插件生成入口,都可能让依赖第一次没被发现。页面访问到新依赖后,Vite 会重新优化并刷新页面。

ts
import { defineConfig } from 'vite' export default defineConfig({ optimizeDeps: { include: ['lodash-es', 'dayjs/plugin/utc'] } })

include 适合提前加入确定会用、但扫描器可能看不到的依赖。不要把所有依赖都塞进去,预构建越多,冷启动和缓存失效时等待越明显。

exclude 不是性能开关

exclude 用来排除不该预构建的依赖。比如某个包必须保持源码形态给插件处理,或者预构建后模块结构出错,才适合排除。

ts
export default defineConfig({ optimizeDeps: { exclude: ['some-vite-plugin-runtime'] } })

常见误区是觉得大型依赖排除后一定更快。实际上它可能产生更多浏览器请求,或者导致 CommonJS 无法直接运行。是否排除要看错误信息和模块格式,不要只看包体积。

esbuildOptions 只做兼容修正

预构建由 esbuild 执行,因此可以通过 optimizeDeps.esbuildOptions 做少量定制,例如 define、loader 或 esbuild 插件。

ts
export default defineConfig({ optimizeDeps: { esbuildOptions: { define: { global: 'globalThis' }, loader: { '.js': 'jsx' } } } })

边界是它只面对依赖预构建,不等同于整个项目的编译配置。修依赖兼容性可以,用它替代 Babel 或框架插件就不合适。

monorepo 更容易踩坑

在 monorepo 里,workspace 包可能既像源码,又像依赖。Vite 对链接依赖通常会按源码处理,方便调试,但深层依赖可能没有被预构建。

ts
export default defineConfig({ optimizeDeps: { include: ['@acme/shared > lodash-es'] }, server: { fs: { allow: ['..'] } } })

本地包的取舍是调试便利和启动性能。复用包最好明确输出 ESM,并写清 exportsmoduletypes

追问

预构建和生产打包是一回事吗?

不是。预构建主要服务开发环境,让依赖能被浏览器快速加载;生产构建由 Rollup 负责,目标是输出可部署产物。踩坑点是以为 optimizeDeps 能控制生产分包,实际生产分包要看 build.rollupOptions

为什么启动后页面会自动刷新一次?

通常是首次扫描没发现某个依赖,页面访问到它时才触发重新预构建。预构建完成后模块地址变化,浏览器需要刷新。可以把这个依赖加入 include,用一点冷启动时间换更稳定的开发过程。

exclude 大依赖能减少时间吗?

有时能,但不是通用优化。排除后浏览器可能请求更多文件,Vite 也可能做更多运行时转换。只有预构建导致兼容问题,或依赖必须保持源码给插件处理时,才适合 exclude。

CommonJS 包为什么依赖预构建?

浏览器不能直接执行 requiremodule.exports。Vite 用 esbuild 把 CommonJS 转成 ESM,开发环境才能加载。边界是转换不能替代 Node 运行时能力,依赖如果需要 fspath,前端仍要换库或做 polyfill。

标签:Vite