先说结论:

Webpack 并不是“慢工具”,它是为了解决旧时代浏览器能力不足的问题而诞生的完整构建方案;Vite 之所以快,本质是因为它把“开发阶段的打包”移除了,改为利用现代浏览器原生能力按需加载与即时编译。


一、从一个疑问开始

我一开始接触 Vite 的时候,最直观的感受就是:启动几乎是秒开的,而 Webpack 项目往往需要等待一段时间。后来回头看才慢慢明白,这并不是简单的“新旧工具之争”,而是两者所处的时代背景完全不同。

Webpack 出现时,浏览器能力还很有限;而 Vite 出现时,浏览器已经具备了模块加载能力。这一点,几乎决定了它们的架构差异。


二、Webpack 为什么会出现

早期浏览器其实是无法直接运行现代前端代码的,比如下面这种写法在当时是不可行的:

import Vue from 'vue'
import App from './App.vue'

因为浏览器当时并不支持 ES Module(import / export),也无法识别 .vue、.scss、.ts、JSX 等文件,更没有模块依赖管理能力。

所以必须有一个工具,在代码运行之前,把这些“浏览器看不懂的东西”全部转换掉。Webpack 正是在这个背景下出现的,它承担了所有“兼容层”的工作。


三、Webpack 实际做了什么

从实际使用角度来看,我更倾向把 Webpack 理解为一个“构建流水线”。它在启动时会完成一整套处理流程,包括分析依赖关系、把多个模块打包成 bundle.js、把 ES6+ 转成低版本 JavaScript、处理 CSS / Sass / 图片资源、压缩代码、生成 HTML 并自动注入资源等。

也就是说,在那个时代,浏览器做不了的事情,Webpack 都帮你做完了。这也是为什么它在当时是刚需,而且设计非常完整。


四、问题从哪里开始出现

随着浏览器不断升级,情况发生了变化。现代浏览器已经支持 <script type="module">,也可以直接使用 import 加载模块。

这意味着:浏览器已经具备“模块加载能力”了。

我在理解这一点之后,才意识到一个关键问题:既然浏览器可以自己加载模块,那开发环境为什么还要先把整个项目打包一遍?

Vite 的思路,其实就是从这里开始的。


五、Webpack 在开发环境为什么慢

假设一个项目有 2000 个模块,当执行 npm run dev 时,Webpack 通常会经历一整套流程:扫描文件、建立依赖图、执行 loader 转换、运行 plugin、进行 chunk 拆分、最后输出 bundle 到内存并启动服务。

这里一个很容易忽略的点是:启动慢并不是因为 dev server 慢,而是因为在服务启动之前,整个项目已经被“完整处理了一遍”。

也就是说,即使你只是打开首页,Webpack 也必须先把所有模块准备好。这也是大型项目中常见的现象,比如启动需要几十秒,修改一个文件热更新也要几秒。


六、慢的本质拆开看

我后来把这个过程拆开来看,基本可以分成几个关键开销。

第一是依赖图构建。Webpack 会从入口文件递归解析所有 import,例如从 main.js 一路找到 App.vue、router、store,再继续向下递归,这个过程涉及文件读取、路径解析、依赖去重等操作,项目越大,这一步越重。

第二是 loader 转换,这一部分通常最吃 CPU。Webpack 默认只认识 JS 和 JSON,像 .vue、.ts、.scss 等文件都需要通过 loader 处理,一个文件可能要经过多轮转换,例如 vue-loader、ts-loader、sass-loader、postcss-loader 等,其中 TypeScript 编译、Sass 编译、Babel 转译、SourceMap 生成都是高开销操作。

第三是 plugin 执行。插件会介入整个构建生命周期,可能涉及文件系统操作、资源压缩、模块遍历、额外校验等,插件越多,整体构建时间越不可控。

最后是输出阶段,虽然开发环境通常写入内存,但在这之前所有构建流程已经完成,所以瓶颈并不在“写文件”,而在前面的处理链路。


七、Vite 的思路完全不同

Vite 在开发环境下几乎反其道而行之。它不会在启动时扫描整个项目,也不会提前打包所有模块,而是只做三件事:启动一个本地服务、预构建 node_modules、等待浏览器请求。

整体流程可以理解为:服务先启动,浏览器访问页面时按需加载模块,Vite 在请求到来时再进行即时编译,并通过 HMR 精准更新。

核心区别可以简单概括为:Webpack 是“先全部准备好再启动”,而 Vite 是“先启动,用到什么再处理什么”。


八、Vite 为什么能做到快

我在实际使用中,对它的“快”主要有几个直观理解。

首先是启动成本极低。执行 npm run dev 时,Vite 主要是启动 HTTP 服务、读取配置、初始化插件系统,这些操作都比较轻,所以几乎可以立即访问本地地址。

其次是依赖预构建。启动时常见的 “Optimizing dependencies...” 实际是在用 esbuild 处理 node_modules,把依赖转换为浏览器可直接运行的 ESM 格式,并进行缓存,后续启动会更快。

然后是按需编译。当浏览器请求 /src/main.js 时,Vite 才处理这个文件,并根据 import 继续请求其他模块。如果某些页面或组件当前没有被访问,比如一些后台页面或大屏模块,它们在此时是完全不会参与编译的。

最后是 HMR 的优化。当修改某个组件时,Vite 只会重新编译这个文件,并通过 websocket 通知浏览器做局部替换,而不是像 Webpack 那样可能触发更大范围的重新构建,所以更新体验会更流畅。


九、在项目规模上的差异

在小项目中,两者的差距其实不算特别明显,比如 Webpack 启动 3 秒,Vite 可能 1 秒左右。

但在大型项目中,这种差距会被明显放大,比如 Webpack 可能需要几十秒启动,而 Vite 仍然可以在几秒内完成。这种差异,本质上来自“是否需要在启动时处理整个项目”。


最后总结

回过头来看,我更倾向这样理解这两者的关系:Webpack 是在浏览器能力不足的时代,提供的一套完整构建解决方案,它解决的是“浏览器做不到怎么办”的问题;而 Vite 是在浏览器能力增强之后,利用原生能力进行优化,它解决的是“既然浏览器能做,为什么还要提前做”的问题。

Webpack 在开发环境下慢,是因为它选择在启动时把所有事情做完;Vite 快,是因为它把这些工作延后,在真正需要时再去处理,这也是两者在体验上产生明显差异的根本原因。