Skip to content
On this page

2. 核心数据流与模块图

在上一章,我们聊到 Vite 像一个聪明的“乐高积木供应商”,能按需递给你需要的模块。但你可能会好奇:Vite 是如何知道“城堡大门”和“塔楼”之间存在关联的?当我修改了“塔楼”的设计图,它又是如何只通知与“塔楼”相关的部分进行更新,而不是重建整个城堡的呢?

答案就藏在 Vite 精心维护的一张“地图”里——模块依赖图(Module Dependency Graph)

什么是模块图?

想象一下你的社交网络。你关注了一些人,也有一些人关注你,这样就形成了一张复杂的人际关系网。在前端项目中,文件与文件之间也存在类似的关系。

  • main.js 引入了 App.vueutils.js
  • App.vue 又引入了 Header.vueFooter.vue

这些 import 关系就像社交网络中的“关注”,将一个个独立的模块文件连接成一张网。这张网,就是模块图。

模块图是 Vite 理解你项目结构的核心。 它记录了每个文件(模块)是谁,它依赖谁(imports),以及谁依赖它(importers)。

Vite 如何构建模块图?

Vite 构建模块图的过程,是一个“顺藤摸瓜”的探索过程。它并不会在启动时就去分析整个项目,而是从“入口”开始,随着你的“访问”逐步展开。

这个过程的数据流大致如下:

  1. 浏览器发起请求:你打开 http://localhost:3000,浏览器首先会请求 index.html

  2. Vite 拦截并转换:Vite 开发服务器接收到请求。对于 index.html,它会找到其中的 <script type="module" src="/src/main.js"></script>,这是探索的起点。

  3. 请求入口模块:浏览器解析 HTML 后,会接着请求 /src/main.js

  4. 按需编译与依赖分析:Vite 拿到 main.js 的代码,对其进行编译(如果需要的话),然后用一个轻量的解析器(例如 es-module-lexer)快速扫描代码中的 import 语句。这时,Vite 知道了:哦,main.js 依赖 App.vueutils.js

  5. 更新模块图:Vite 在模块图中创建 main.js 的节点,并记录下它的依赖项。同时,它会重写 main.js 中的导入路径,例如将 import App from './App.vue' 转换为 import App from '/src/App.vue',确保浏览器能正确请求。

  6. 返回代码给浏览器:Vite 将处理后的 main.js 代码返回给浏览器。

  7. 循环探索:浏览器接收到 main.js 后,会根据其中的 import 语句,继续请求 /src/App.vue/src/utils.js。Vite 会重复步骤 4-6,继续探索并扩展模块图,直到所有被访问到的模块都记录在案。

这个过程就像是在绘制一张寻宝图,从起点出发,每找到一个线索(import),就在图上标记出来,并沿着线索继续寻找,直到整个宝藏(你的应用)的地图被完整地绘制出来。

模块图与 HMR

模块图最重要的作用,体现在**热模块更新(HMR)**上。

当你修改了 Header.vue 文件时:

  1. Vite 的文件监听器捕捉到了这个变化。
  2. 它立刻在模块图中找到了 Header.vue 对应的节点。
  3. 通过模块图,Vite 知道 App.vue 引入了 Header.vueApp.vue 就是 Header.vueimporter(导入者)。
  4. Vite 会顺着这条“依赖链”向上查找,看看这个变更会影响到谁。在这个简单的例子里,影响链是 Header.vue -> App.vue -> main.js
  5. Vite 通过 WebSocket 向浏览器客户端发送一条消息,告诉它:“Header.vue 更新了,请重新请求它和它的父模块 App.vue”。
  6. 浏览器收到消息后,重新请求相关模块,并由框架(如 Vue、React)执行“热替换”逻辑,最终只更新页面上与 Header.vue 相关的部分,而无需刷新整个页面。

正是因为有了这张精确的模块图,Vite 才能在代码变更时,实现“外科手术式”的精准更新,而不是“推倒重来”。

理解了数据流与模块图,你就掌握了 Vite 高效运行的第一个核心秘密。在接下来的章节中,我们将深入到代码层面,看看 Vite 是如何具体实现这些流程的。

2. 核心数据流与模块图 has loaded