创见博客
Vite 和 webpack 的区别:从工作原理讲清楚
七崽爱吃小饼干2026/09/20阅读 0

前面几篇都在讲 webpack 的 loader 和 plugin。但如今新建项目,很多人第一反应是 Vite。于是「Vite 和 webpack 到底差在哪」成了绕不开的问题。本文不站队,只从工作原理出发,把两者的差异讲清楚。

一、先看结论

维度webpackVite
开发启动先打包,再启动服务不打包,按需编译
依赖处理全部进入 bundleesbuild 预构建,浏览器原生 ESM 加载
冷启动速度随项目规模线性变慢基本恒定,与模块数弱相关
HMR重建模块图后推送沿 ESM 边界精确更新
生产构建webpack 打包Rollup 打包
配置风格强大但繁琐约定优先,开箱即用
插件机制Tapable 钩子Rollup 兼容插件 + Vite 专属钩子
浏览器兼容可兼容旧浏览器开发阶段需原生 ESM
dev/prod同一套引擎,一致性高开发 ESM、生产 Rollup,可能有细微差异

一句话:webpack 是「先打包,再运行」,Vite 是「先运行,用到再编译」。

二、webpack 是怎么工作的

2.1 一切皆模块,先打包再运行

webpack 的核心模型是依赖图:从 entry 出发,递归解析 import / require,把所有模块(JS、CSS、图片……)经过 loader 转换后,拼装成一个或多个 bundle。

浏览器不认识 import './style.css',也不认识裸模块名 import Vue from 'vue'。这些都需要 webpack 在构建期解析、替换、合并,最终产出一份浏览器能直接执行的 JS。

所以 webpack 的产物必须「先构建完成」才能运行。

2.2 dev server 也在打包

启动 webpack-dev-server 时,webpack 并没有偷懒:它照样跑一遍完整构建,只是把产物放在内存里而不是磁盘。浏览器请求 bundle.js,dev server 直接返回内存里的结果。

js
// webpack.config.js
module.exports = {
  entry: './src/main.js',
  devServer: { hot: true, port: 8080 },
};

这就解释了一个现象:项目越大,npm run dev 越慢——因为服务的启动时间几乎等于一次完整打包的时间。webpack 5 的持久化缓存(cache: { type: 'filesystem' })能缓解二次启动,但首次启动依然要全量构建。

2.3 HMR 的代价

修改一个文件后,webpack 需要在依赖图里找出受影响的模块并重新构建相关 chunk,再把更新推给浏览器。依赖图越大,这个「找影响范围 + 重建」的过程越慢。

三、Vite 是怎么工作的

3.1 开发:不打包,按需编译

Vite 开发时直接把源码交给浏览器。因为现代浏览器原生支持 ESM,<script type="module" src="/src/main.js"> 就能工作。当浏览器执行到 import 时,会向 dev server 发请求,Vite 这时才即时编译这一个文件并返回。

md
浏览器 → 请求 /src/main.js → Vite 编译并返回
      → 遇到 import './App.vue' → 再请求 → Vite 编译该文件

也就是说,只有被真正请求到的模块才会被编译。页面首屏用了多少模块,就编多少模块,与项目总规模无关——这就是 Vite 冷启动快的根本原因。

3.2 依赖预构建:解决「裸模块」问题

浏览器不认识 import Vue from 'vue' 这种裸模块名,而且一个依赖往往内部有几十上百个文件,逐个请求会造成请求风暴。

Vite 的做法是用 esbuild 先做一次「依赖预构建」:

  • 把 CommonJS / UMD 依赖转成 ESM;
  • 把零散的内部模块合并成一个文件,减少请求数;
  • 结果缓存在 node_modules/.vite,依赖不变就复用。

esbuild 用 Go 编写,比 JS 实现的打包器快一到两个数量级,所以这一步通常很快。

3.3 生产:换成 Rollup

开发阶段不打包是为了快,但生产环境需要 tree-shaking、代码分割、压缩等优化,所以 Vite 在生产构建时换用 Rollup:

bash
vite build   # 底层是 Rollup

这也是 Vite 与 webpack 的一个重要差异:开发和生产的底层引擎不是同一个。

四、核心差异逐项看

4.1 启动速度

  • webpack 的启动成本 = 一次完整打包,与项目规模正相关。
  • Vite 的启动成本 = 启动服务 + 依赖预构建,与业务代码规模基本无关。

项目越大,差距越明显。小项目里两者体感接近。

4.2 热更新 HMR

  • webpack:文件改动后,沿依赖图反向查找受影响的模块,重新构建对应部分,通过 HMR runtime 推送到浏览器。模块图越大,定位越慢。
  • Vite:利用 ESM 的模块边界,只让改动模块及其直接引用者失效,请求该模块时重新编译。更新范围小且精确。

Vite 的 HMR 通常更快,但两者在现代项目中都能达到「毫秒级」体感,webpack 的差距在大项目里才明显。

4.3 生产构建

webpackVite
引擎webpackRollup
Tree-shaking支持(依赖 ESM)支持,Rollup 更彻底
代码分割成熟,策略丰富支持,开箱即用
产物优化插件生态极丰富依赖 Rollup 插件
Module Federation原生支持需第三方插件,支持有限

生产构建上 webpack 依旧强势:Module Federation(模块联邦)、复杂分包策略等高级能力,webpack 的生态更成熟。

4.4 配置风格

webpack 的配置是出了名的多:

js
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[hash].js', clean: true },
  module: {
    rules: [
      { test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' },
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
      { test: /\.(png|svg)$/, type: 'asset/resource' },
    ],
  },
  plugins: [new HtmlWebpackPlugin({ template: './index.html' })],
  devServer: { hot: true },
};

同样的项目,Vite 的配置是:

js
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
});

Vite 把大量约定内置(入口 index.html、CSS/图片/TS 开箱可用),配置量显著减少。代价是想深度定制时,webpack 的显式配置反而更可控。

4.5 插件机制

  • webpack:基于 Tapable 的钩子系统,插件可以介入从启动到产物落盘的任意阶段,控制粒度极细,但学习曲线陡。
  • Vite:兼容大部分 Rollup 插件接口,同时提供 Vite 专属钩子(config、transformIndexHtml、handleHotUpdate、configureServer 等)。写起来更简单,但开发态与构建态钩子的语义差异需要留意。

如果你熟悉 webpack 的 loader,可以把 Vite 的插件理解为「Rollup 插件 + dev server 增强」的组合。

4.6 浏览器兼容

  • webpack:产物是普通 bundle,配合 Babel 可以兼容老浏览器。
  • Vite:开发态依赖浏览器原生 ESM,新项目开发默认面向现代浏览器;生产产物可以用 @vitejs/plugin-legacy 转译以兼容旧浏览器。

如果必须支持 IE 或很旧的移动端浏览器,webpack 更省心。

4.7 dev/prod 一致性

这是 Vite 常被讨论的一点:

  • webpack 开发和构建都用同一套引擎,行为基本一致;
  • Vite 开发用 esbuild + 原生 ESM,生产用 Rollup,两个引擎可能产生细微差异(如 CSS 处理顺序、import.meta 行为等),偶尔出现「开发正常、构建出错」。

Vite 团队一直在收敛这些差异,但作为使用者需要知道这个前提。

五、到底该选谁

场景推荐
新项目、现代浏览器、追求开发体验Vite
Vue / React / Svelte 等标准 SPAVite
需要兼容旧浏览器webpack
需要 Module Federation、微前端webpack
深度定制构建流程、复杂 loader 链webpack
老项目已有完整 webpack 配置继续 webpack,或渐进迁移
库开发(需要精细产物控制)两者皆可,Rollup / tsup 也常见

一个务实的态度:Vite 解决的是「开发体验」,webpack 解决的是「构建能力上限」。大多数业务项目 Vite 足够;需要复杂构建编排时,webpack 仍是更稳的选择。

六、小结

  • webpack:先打包再运行,依赖图 + loader + Tapable 插件,配置强大、生态成熟,开发启动随规模变慢。
  • Vite:开发不打包,按需编译,用 esbuild 预构建依赖、浏览器原生 ESM 加载;生产用 Rollup。
  • 核心差异在开发模式:webpack 全量构建,Vite 按需编译。
  • 选择取决于场景:追求开发体验选 Vite,需要兼容性与复杂构建能力选 webpack。

理解了两者的模型差异,再回头看 webpack 的 loader 和 plugin 机制,你会发现它们解决的是同一类问题的两种思路——这也是这个系列先讲 webpack 的原因。

评论
0/100