Skip to content

Vite 详解

Vite 不是“一个更快的打包工具”这么简单。

更具体一点,Vite 是一套围绕:

  1. 现代 ESM 开发工作流
  2. 快速本地开发反馈
  3. 按需模块处理
  4. 构建与开发职责分离

组织起来的前端开发与构建方案。

如果只压缩成一句话,可以记住:Vite 的核心价值,是用现代模块机制重构本地开发体验,并把构建职责交给更适合生产产物输出的链路完成。

1. Vite 在解决什么问题

Vite 出现之前,很多前端应用在开发阶段就已经遇到了明显问题:

  1. 项目越大,启动越慢
  2. 改一行代码,热更新反馈也可能越来越慢
  3. 开发阶段就要先经历一轮比较重的打包过程

Vite 真正想解决的是:为什么本地开发也要把整个项目打成一大坨,才能开始改代码。

所以它的设计重点并不是“全面替代一切构建能力”,而是:

  1. 让开发阶段尽可能快
  2. 让模块按需处理
  3. 把开发和生产构建的关注点分开

2. Vite 的核心心智模型是什么

理解 Vite,最重要的是先接受一个和传统重型打包开发流不同的思路:开发阶段不急着把整个应用先打包完,而是尽量利用浏览器原生 ESM 能力,按需处理模块。

看一条简化主线:

mermaid
flowchart TD
    A[浏览器请求入口模块] --> B[Vite 开发服务器拦截请求]
    B --> C[按需转换当前模块]
    C --> D[把可运行结果返回浏览器]
    D --> E[后续依赖再被继续按需请求]

最值得先记住的是:Vite 开发阶段强调的是“按请求驱动的模块处理”,而不是“先全量打包再运行”。

3. 为什么 Vite 本地开发通常更快

它的快,主要不是因为“实现更神秘”,而是因为它减少了开发阶段的全量工作量。

可以这样理解:

  1. 浏览器自己就能理解 ESM
  2. 那开发服务器就不必把所有业务模块都打包成 bundle
  3. 当前页面真正请求到哪个模块,再处理哪个模块

所以它尤其擅长:

  1. 大中型前端应用的本地开发
  2. 频繁修改组件和页面时的快速反馈
  3. 现代前端工作流

4. Vite 的开发阶段和生产阶段为什么要分开理解

这是理解 Vite 非常关键的一点。

4.1 开发阶段

开发阶段更关注:

  1. 启动快
  2. 模块按需转换
  3. HMR 反馈快

4.2 生产阶段

生产阶段更关注:

  1. 打包产物
  2. 代码分割
  3. 压缩和缓存
  4. 最终部署输出

也就是说:Vite 的开发体验优势,不能简单等同于生产构建就天然覆盖所有优化问题。

4.3 为什么开发阶段可以尽量不先全量打包

这件事如果只理解成“浏览器支持 ESM,所以不用打包”,还是不够完整。

更具体一点,Vite 开发阶段能尽量不先全量打包,依赖的是三件事同时成立:

  1. 浏览器本身已经能加载 ESM
  2. 开发服务器可以拦截请求并动态转换源码
  3. 项目里的模块关系可以通过 import 被持续分析出来

这意味着开发服务器要做的,不是预先生成整个应用的最终 bundle,而是:

  1. 浏览器请求到哪个模块,就处理哪个模块
  2. 把源码里的依赖路径改写成浏览器下一跳还能继续请求的地址
  3. 在必要时对框架文件、TypeScript、JSX 等做即时转换

所以:Vite 不是不处理源码,而是把“全量前置打包”改成了“按请求驱动的动态转换”。

4.4 为什么生产阶段仍然要打包

很多人第一次接触 Vite,最容易产生一个误解:既然开发阶段已经可以按需加载模块,为什么生产阶段还要重新构建。

原因其实很现实,因为浏览器线上运行关注的不是“开发方便”,而是:

  1. 请求数量不能过碎
  2. 首屏资源不能过大
  3. 缓存命中要稳定
  4. 静态资源引用关系要可部署、可预测

如果把开发阶段那种“浏览器按模块逐个请求”的方式直接搬到线上,通常会遇到:

  1. 请求过多
  2. 网络往返成本变高
  3. 第三方依赖难以形成稳定缓存策略
  4. 部署产物结构不够集中

所以更具体一点:Vite 不是取消打包,而是把“开发阶段先打整包”这件事尽量拿掉,把打包集中到真正需要产物优化的生产阶段。

5. 一份最小但完整的 Vite 配置应该怎么看

很多人第一次接触 Vite,会觉得它“配置少很多”,于是容易忽略配置背后的真实职责。

但如果从工程角度看,一份 Vite 配置通常还是在回答这些问题:

  1. 项目根目录在哪里
  2. 开发服务器怎么跑
  3. 别名和模块解析怎么组织
  4. 插件在做什么转换
  5. 生产构建怎么输出

例如:

ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
  server: {
    port: 5173,
    open: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
      },
    },
  },
  build: {
    outDir: 'dist',
    sourcemap: true,
  },
})

这类配置真正表达的是:

  1. 当前项目开发态要接入哪些能力
  2. 浏览器请求模块时,开发服务器要如何响应
  3. 生产阶段产物应该如何输出

5.1 resolveserver 为什么仍然很关键

虽然 Vite 配置整体更轻,但不代表这些配置就不重要。

resolve 会直接影响:

  1. 别名是否一致
  2. 模块路径是否稳定
  3. 开发和构建阶段解析结果是否一致

server 会直接影响:

  1. 本地启动端口
  2. 接口代理
  3. 跨域联调体验
  4. history 路由兜底

所以:Vite 配置少,并不代表可以忽略配置边界;只是它把默认值做得更贴近现代前端项目。

6. 依赖预构建到底是什么

这是 Vite 里非常关键、也非常容易被忽略的一环。

可以这样理解:依赖预构建,就是把第三方依赖处理成更适合浏览器开发阶段直接消费的形式。

它主要解决的不是业务源码,而是第三方包在开发阶段带来的这些问题:

  1. 依赖内部模块过多,浏览器直接请求会很碎
  2. 某些包仍然带有 CommonJS 等不够友好的形态
  3. 重复处理第三方依赖会拖慢启动和刷新体验

6.1 为什么 Vite 要单独处理依赖

Vite 的开发阶段强调“按需请求业务模块”,但第三方依赖往往具有两个特点:

  1. 更新频率远低于业务代码
  2. 体积和内部依赖关系往往更复杂

所以更合理的做法通常是:

  1. 业务模块继续按需处理
  2. 第三方依赖先做一轮预处理

这样可以减少浏览器请求碎片,也能把 CommonJS 等形态先转得更适合开发阶段使用。

6.2 预构建为什么能让冷启动更稳

Vite 的冷启动体验好,除了按需处理业务模块之外,依赖预构建也是重要原因之一。

它的价值通常体现在:

  1. 第三方依赖不用每次都重新零散转换
  2. 浏览器请求数量更可控
  3. 常见依赖兼容性问题更早暴露

更具体一点:Vite 不是完全不做前置工作,而是把前置工作集中在更值得预处理的依赖层。

6.3 依赖预构建在实现上通常是怎么做的

如果再往下理解一层,可以把依赖预构建看成两步:

  1. 先扫描项目入口里引用了哪些第三方依赖
  2. 再把这些依赖转换成更适合开发服务器直接提供给浏览器的结果

在实现层面,Vite 会借助更快的转换工具先处理依赖,把 CommonJS、多个零散内部模块以及某些不够适合浏览器直接消费的形态,提前整理成更稳定的开发态结果。

这背后的核心思想不是“优化所有东西”,而是:

  1. 第三方依赖相对稳定,值得提前处理
  2. 业务代码改动频繁,不适合每次都做重型前置工作

这也是为什么很多时候你第一次启动项目会看到预构建动作,后续启动体验则会更平稳。

7. Vite 开发服务器请求链路到底是怎么跑的

理解这一段之后,很多“为什么它快”“为什么这里会失效”的问题会更容易想明白。

看一条简化链路:

mermaid
flowchart TD
    A[浏览器请求 index.html] --> B[Vite 返回开发态 HTML]
    B --> C[浏览器继续请求入口模块]
    C --> D[Vite 解析 import 依赖]
    D --> E[按需转换当前请求模块]
    E --> F[把 ESM 结果返回浏览器]
    F --> G[浏览器继续发起下一层模块请求]

这条链路最值得记住的是:

  1. 浏览器本身参与了模块加载
  2. Vite 更像按请求动态响应的开发服务器
  3. 它不是把整棵业务模块树都打成一个 bundle

7.1 index.html 在 Vite 里为什么更像入口的一部分

在很多传统工作流里,HTML 更像最终产物模板。

但在 Vite 开发阶段,index.html 的地位更靠前,因为它常常直接作为开发服务器处理入口之一,参与:

  1. 脚本注入
  2. 环境替换
  3. 入口模块声明

所以:在 Vite 里,HTML 不是纯静态壳子,它本身就是开发链路的一部分。

7.2 Vite 为什么要做 import 分析和路径改写

如果开发服务器只是把源码原样返回给浏览器,其实很多项目并不能直接跑起来。

因为源码里的 import 往往还带着这些问题:

  1. 第三方依赖路径并不是浏览器天然可识别的 URL
  2. 某些模块需要插入 HMR 相关逻辑
  3. 某些框架文件在返回前还要先经过转换

所以 Vite 很重要的一步是:在返回模块内容之前,先分析 import 依赖,并把它们改写成浏览器下一跳还能继续请求的开发态地址。

这一步非常关键,因为它决定了:

  1. 模块图怎样在开发阶段被串起来
  2. 浏览器下一次请求会打到哪里
  3. HMR 更新时哪些模块会被视为上下游关系

7.3 为什么说 Vite 其实也维护着一张开发态模块图

很多人会以为 Vite 因为“不先打整包”,所以就没有类似模块图的概念。

这并不准确。

它虽然不一定像传统打包阶段那样先全量产出 bundle,但开发服务器仍然需要持续理解:

  1. 当前模块依赖了谁
  2. 谁又反过来依赖当前模块
  3. 某个文件变化之后,应该通知哪些上游模块失效

也就是说:Vite 不再把“先全量生成产物”作为开发前提,但它依然需要维护模块依赖关系,才能支撑按需转换与 HMR。

8. HMR 为什么通常更快、更轻

Vite 的 HMR 体验之所以常常很好,不只是因为“实现更快”,而是因为它减少了不必要的重编译范围。

看一条简化主线:

mermaid
flowchart TD
    A[修改某个源码模块] --> B[Vite 识别受影响模块边界]
    B --> C[只重新转换相关模块]
    C --> D[通过 HMR 通知浏览器]
    D --> E[浏览器替换对应模块]
    E --> F[页面尽量保留现有状态]

这背后的关键点通常有两个:

  1. 模块边界更清楚
  2. 不需要先重新打出整包结果

8.1 为什么 HMR 仍然可能退化

即使使用 Vite,也不是所有改动都一定能优雅热更新。

常见影响因素包括:

  1. 模块副作用过重
  2. 某些状态绑定方式不利于局部替换
  3. 插件转换结果让模块边界变得不够稳定

所以更具体一点:Vite 的 HMR 通常更快,但它依然依赖模块组织方式和框架集成质量。

8.2 一个 HMR 更新为什么会沿着依赖关系向上冒泡

很多人看到热更新失败时,会觉得“只是一个文件改了,为什么最后还是整页刷新”。

原因通常在于,HMR 并不只是发现“哪个文件变了”,它还要继续判断:

  1. 这个模块能不能被自身安全替换
  2. 它的上游调用者能不能接受这次更新
  3. 当前变更会不会破坏已有状态或副作用关系

如果某个更新边界无法被局部消化,更新就会继续向上冒泡,直到:

  1. 某个模块接受这次热更新
  2. 或者最终退化成整页刷新

所以从排障角度看,很多 HMR 问题本质上不是“工具没刷新成功”,而是:你的模块边界、副作用和状态依赖关系,不适合被局部替换。

9. Vite 为什么特别适合现代应用开发

它的优势通常集中在这些方面:

  1. 冷启动快
    项目启动时不必先全量打包业务代码。

  2. 热更新反馈好
    修改局部模块时,通常更容易做到快速反馈。

  3. 更贴近现代 ESM 工作流
    开发阶段的模块组织思路更自然。

  4. 对 Vue、React、TypeScript 等现代生态支持友好
    新项目接入成本通常较低。

10. Vite 的限制和边界是什么

如果只看到“快”,也很容易失真。

Vite 的限制和边界同样要看清:

  1. 它更偏现代前端生态 对老旧环境、重历史包袱项目,不一定总是最省心的迁移选择。

  2. 某些高度定制构建场景未必像 Webpack 一样从容 当构建链异常复杂时,Webpack 的强控制能力仍然有价值。

  3. 开发快,不代表所有生产问题都自动解决 生产阶段仍然需要关注拆包、缓存、产物质量和依赖体积。

  4. 项目迁移时要考虑插件生态和历史配置迁移成本 尤其老项目从 Webpack 切到 Vite 时,这个成本不能低估。

11. Vite 典型适合什么场景

Vite 更适合:

  1. 新建现代前端应用
  2. 强调本地开发效率的团队
  3. Vue、React、TypeScript 主线的项目
  4. 不希望开发阶段再背负过重打包启动成本的场景

它尤其适合作为:现代应用开发的高效默认选项。

12. 插件体系在 Vite 里扮演什么角色

Vite 的“轻”,并不意味着它没有扩展能力。

相反,它也有很重要的插件体系,只是它的扩展重点更贴近现代开发流。

插件常见会参与:

  1. 框架文件处理,例如 Vue、React
  2. JSX、TS、Markdown 等源码转换
  3. 虚拟模块注入
  4. 开发服务器钩子扩展
  5. 构建阶段产物处理

12.1 为什么说插件是 Vite 能力边界的重要组成部分

Vite 本体提供的是核心工作流,但很多工程能力真正落地,依赖的仍然是插件生态。

这意味着:

  1. 新项目上手会很顺
  2. 复杂需求仍然要回到插件能力评估
  3. 迁移项目时也要检查原来依赖的构建扩展能否平滑迁过来

所以:不要把 Vite 理解成“没有构建扩展成本”,而是它把常见场景做成了更轻的默认路径。

13. 生产构建阶段到底是谁在负责什么

很多人只记住“Vite 开发快”,却没有把生产构建阶段想清楚。

更具体一点,Vite 生产阶段关注的是:

  1. 生成最终可部署产物
  2. 做代码分割和资源输出
  3. 产出适合上线的静态文件

在工程语境里,也可以这样理解:Vite 把开发服务器体验做成自己的主线,而生产构建阶段则借助更成熟的打包产物能力来完成最终输出。

也就是说:Vite 的核心亮点首先在开发阶段,但生产阶段同样是一条完整构建链。

13.1 生产阶段要重点关注哪些问题

即使用 Vite,到了生产阶段也仍然要关心:

  1. chunk 是否拆得合理
  2. 静态资源是否过大
  3. 是否需要 Sourcemap 便于排障
  4. 动态导入是否形成了稳定的异步边界
  5. 第三方依赖是否被过度打进首屏路径

所以工程上更常见的做法是:

  1. 开发阶段先保证反馈快
  2. 生产阶段再针对产物结构和缓存策略做专项治理

14. 一条更实用的 Vite 优化路线

讨论 Vite 优化时,不能只停留在“它已经很快了”。

更具体一点,Vite 优化通常也分成几类目标:

  1. 依赖预处理与开发体验优化
  2. HMR 反馈优化
  3. 生产构建产物优化

14.1 开发阶段优化

开发阶段通常更关注:

  1. 第三方依赖预处理是否合理
  2. 模块请求数量是否过于碎片化
  3. HMR 边界是否清楚

14.2 生产阶段优化

生产阶段仍然要关注:

  1. chunk 切分
  2. 资源体积
  3. 缓存命中
  4. 动态加载边界

也就是说:Vite 的“快”主要首先体现在开发阶段,但线上性能依然需要认真经营。

14.3 一份更落地的优化清单

如果你已经确定要对 Vite 做专项治理,通常可以按下面顺序逐项排:

  1. 看启动慢是不是出在依赖预构建
  2. 再看 HMR 变慢是不是因为模块边界或插件转换过重
  3. 再看生产构建里 chunk 是否切得过碎或过大
  4. 再看第三方重依赖是否真的都应该进入首屏路径
  5. 最后再看 Sourcemap、缓存命名和静态资源体积

15. 从 Webpack 迁移到 Vite 时要看什么

迁移这件事不能只看“能不能跑起来”,还要看工程链路是否真的迁完整。

通常至少要检查:

  1. 别名、环境变量和静态资源路径是否兼容
  2. 原有 Loader / Plugin 对应能力是否能在 Vite 里找到实现
  3. 开发代理、Mock、路径注入等本地联调能力是否补齐
  4. 生产构建输出结构是否满足部署平台要求
  5. 老旧 CommonJS 依赖和历史脚本是否会拖累迁移

15.1 一条更稳的迁移顺序

工程里更常见、也更稳的迁移方式通常是:

  1. 先迁开发链路,确保本地页面和接口联调正常
  2. 再补框架插件、别名和环境变量
  3. 再校验静态资源、样式和按需加载行为
  4. 最后再对生产构建、部署和性能结果做比对

这样做的好处是:

  1. 问题更容易定位
  2. 不会把开发体验问题和线上产物问题混在一起
  3. 团队能更清楚地看到迁移收益和代价

16. Vite 和 Webpack 的核心区别到底是什么

很多人会把它们理解成简单的新旧替代关系,但这不够准确。

更具体一点:

  1. Webpack 更像一套高度可控的通用构建体系
  2. Vite 更像一套以现代开发体验为中心重新设计的工作流

如果只压缩成一句话:Webpack 更强在复杂工程控制力,Vite 更强在现代应用开发体验。

17. 常见误区

17.1 误区一:Vite 快,所以一定全方位更优

不准确。

Vite 的优势非常明显,但并不意味着所有项目、所有历史场景都天然最优。

17.2 误区二:用了 Vite 就不用关心构建优化了

开发快不等于线上产物天然已经最佳。

17.3 误区三:Vite 只是“另一个打包器”

这会低估它的设计重点。

Vite 更关键的变化,其实发生在开发阶段工作流。

17.4 误区四:迁移成本可以忽略

对于历史项目来说,插件、路径、环境变量、资源处理和构建习惯都可能带来迁移工作量。

18. 一份检查清单

理解和使用 Vite 时,至少可以自查:

  1. 当前问题发生在开发阶段还是生产阶段
  2. 项目是否确实适合现代 ESM 工作流
  3. 当前依赖预处理是否合理
  4. HMR 边界是否清楚
  5. 生产产物的拆包和缓存策略是否真正做好
  6. 历史项目迁移时是否评估过插件和配置成本

19. 小结

如果把 Vite 再压缩成一句话,可以记住:

Vite 的强项不只是“快”,而是它把现代前端开发阶段从重打包思维里解放出来,再把生产构建交给更合适的产物输出链路。

基于 VitePress 构建的个人技术笔记。