好问题 👍。
我给你从整体到细节把 Vite 的底层实现原理 梳理一下,分成 开发阶段 和 构建阶段 两部分来看。
一、设计理念
Vite 的核心目标就是:开发时极快冷启动 + 按需编译。
传统的 Webpack 在启动时需要 打包整个项目,Vite 则反其道而行:
- 开发阶段 → 不打包,直接用浏览器原生的
ESM(ES Modules)。 - 生产构建 → 用
Rollup做一次完整打包,保证产物优化。
二、开发阶段(Dev Server)
-
启动 HTTP Server
Vite 本质上就是一个基于 Node.js 的开发服务器。- 内部用
koa风格的中间件系统。 - 负责拦截请求,做文件解析和转换。
- 内部用
-
依赖预构建(Pre-Bundling)
-
用
esbuild(Go 编写,极快)把 第三方依赖(比如react、vue、lodash)打包成一个个 ESM 模块。 -
这样做是因为:
- 依赖一般是 CJS/UMD 格式,不兼容浏览器 ESM。
- 避免一个库里有成千上万个小文件,浏览器请求太慢。
-
预构建后的依赖存放在
node_modules/.vite下。
-
-
源码按需编译
-
当浏览器请求
http://localhost:5173/src/main.ts:- Vite 拦截请求,发现是
.ts文件 → 用esbuild转成 JS。
- Vite 拦截请求,发现是
-
当请求
.vue文件:- Vite 内部调用 Vue 的 SFC 编译器 → 转成 JS 模块。
-
当请求 CSS:
- 转成一个 JS 模块,里面
export default是生成的 CSS 内容,并动态插入<style>。
- 转成一个 JS 模块,里面
👉 所以,Vite 是 “请求一个文件 → 编译一个文件 → 直接返回给浏览器” 。
-
-
HMR(热更新机制)
-
Vite 建立了一个 WebSocket 连接。
-
当源码文件变更时:
- Vite 只重新编译修改过的模块。
- 通知浏览器替换对应模块,而不是刷新整个页面。
-
速度极快,因为不用重新打整个 bundle。
-
三、构建阶段(Build)
-
调用 Rollup
-
Vite 的生产构建是基于 Rollup 的二次封装。
-
好处:
- Rollup 的 tree-shaking 非常强大。
- 插件生态成熟。
-
-
分包策略
- 默认会把
node_modules里的依赖单独拆包(利用 Rollup 的manualChunks)。 - 生成
vendor.[hash].js,提升浏览器缓存利用率。
- 默认会把
-
优化产物
- 压缩(
terser/esbuild)。 - CSS 抽取成单独文件。
- 静态资源(图片、字体)根据大小决定是内联还是生成单独文件。
- 压缩(
四、简化理解
可以把 Vite 理解成:
- 开发时:像一个 ESM 文件服务器,只在请求时用
esbuild/编译器做格式转换,按需返回。 - 生产时:用 Rollup 把所有源码和依赖优化成一个高效可部署的包。
五、对比 Webpack
| 特性 | Vite | Webpack |
|---|---|---|
| 开发启动速度 | 毫秒级(因为不用打包) | 秒级到分钟级 |
| 模块加载 | 浏览器原生 ESM 按需加载 | 先打包成一个或多个 bundle |
| 依赖预处理 | esbuild 预构建 | Loader/Plugin 慢很多 |
| 生产构建 | Rollup (tree-shaking 优秀) | Webpack 自身构建 |
| 热更新 (HMR) | 精准模块替换,极快 | 依赖打包粒度,速度较慢 |
👉 总结一句话:
Vite 开发时就是一个智能的 ESM 文件服务 + 依赖预构建,构建时再用 Rollup 打包优化。