一、项目结构示例
my-project/
├─ node_modules/
├─ src/
│ ├─ main.js # 入口文件
│ ├─ App.vue # 根组件
│ ├─ utils/
│ │ └─ math.js # 工具模块
├─ index.html # 基础HTML
二、完整请求处理流程
- 浏览器请求ESM
当访问http://localhost:3000时,浏览器加载index.html并解析到:
```
<script type="module" src="/src/main.js"></script>
```
触发对`main.js`的ESM请求
-
Vite拦截请求
Vite服务器拦截到/src/main.js请求,检查发现:- 该文件未被预构建(非
node_modules内容) - 未在内存缓存中找到编译结果
进入实时编译流程
- 该文件未被预构建(非
预构建(Pre-Bundling)
- 仅针对`node_modules`中的第三方依赖
- 将CommonJS/UMD模块转换为ESM格式
- 产物存储在`.vite/deps`目录并建立依赖图谱
实时编译(On-Demand Compilation)
- 处理项目源码(如`/src/`下的文件)
- 按需进行TS转译、Vue SFC编译等操作
-
实时编译处理
Vite对main.js进行以下处理:// 原始main.js import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')-
将
'vue'重写为预构建路径/node_modules/.vite/vue.js -
将
App.vue转换为可执行的ESM格式(拆分为script/style/template)
-
-
返回编译结果
生成带HMR支持的编译后代码:import { createApp } from '/node_modules/.vite/vue.js' import App from '/src/App.vue?import' createApp(App).mount('#app') // 注入HMR客户端代码 if (import.meta.hot) { import.meta.hot.accept() }同时将该结果存入内存缓存
实际编译效果如下:
-
后续请求处理
当浏览器请求App.vue时:- 若开发者未修改文件,直接返回缓存结果
- 若文件已修改,重新编译并更新缓存
三、关键场景示例
场景1:修改工具函数
-
修改
src/utils/math.js:- export const add = (a, b) => a + b + export const add = (a, b) => a + b + 1 -
Vite检测到文件变更:
- 使旧缓存失效
- 仅重新编译
math.js及其依赖链 - 通过HMR通知浏览器更新模块
场景2:添加新依赖
-
新增
src/composables/useCounter.js:export default () => { const count = ref(0) const inc = () => count.value++ return { count, inc } } -
首次导入时触发实时编译,后续请求直接读缓存
四、缓存策略对比
| 文件类型 | 缓存策略 | 示例 |
|---|---|---|
| 预构建依赖 | 强缓存(max-age=1年) | /node_modules/.vite/ |
| 业务代码 | 协商缓存(304) | src/components/*.vue |
| 静态资源 | 根据配置决定 | public/logo.png |
通过这种机制,Vite实现了开发环境下的秒级启动和亚秒级热更新。生产构建时则切换为Rollup的完整打包流程以保证最佳性能。