WinddSnow

Vite-Build-Tool-Getting-Started-Dev-Server-HMR-Plugins

字数统计: 3.5k阅读时长: 14 min
2026/07/31

第78课:Vite 构建工具入门——项目初始化、开发服务器、HMR 原理、插件机制

Vite(法语“快”)是由 Vue.js 作者尤雨溪开发的下一代前端构建工具。它由两部分组成:一个基于浏览器原生 ES Modules开发服务器,提供极速的冷启动和热模块替换(HMR);以及一个基于 Rollup生产构建引擎,将代码打包为高度优化的静态资源。Vite 的设计哲学是“利用浏览器已经支持的 ES Modules 来加速开发,仅在打包时才做传统构建”。本节课将系统讲解 Vite 的项目初始化、开发服务器与 HMR 的底层原理、以及插件系统的架构——如何通过 Rollup 插件和 Vite 专有钩子扩展构建流程。


1. Vite 的设计哲学与项目初始化

1.1 Vite 的两大核心组件

组件 底层依赖 职责
开发服务器 esbuild 预构建依赖(将 CJS/UMD 转为 ESM)、极速冷启动、HMR。
生产构建 Rollup 打包优化、Tree Shaking、代码分割、静态资源处理。

开发环境下,Vite 利用浏览器原生 ES Modules(<script type="module">)来提供源码——文件按需编译,无需事先打包整个应用。生产环境下,Vite 使用 Rollup 进行完整的静态分析和打包优化。这种“开发走 native ESM,生产走 Rollup”的双轨策略,使 Vite 在开发体验和最终产出之间取得了最佳平衡。

1.2 创建 Vite 项目

Vite 支持多种前端框架模板,通过 CLI 一键生成:

1
2
3
4
5
6
7
# 使用官方脚手架创建项目
npm create vite@latest my-app -- --template vue-ts

# 支持的模板
npm create vite@latest my-app -- --template react-ts
npm create vite@latest my-app -- --template vanilla
npm create vite@latest my-app -- --template svelte

执行后会自动生成项目骨架,包含 vite.config.tstsconfig.jsonpackage.json 等文件。也可以手动安装 Vite:

1
2
npm init -y
npm install -D vite

然后在 package.json 中添加脚本:

1
2
3
4
5
6
7
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}

vite 启动开发服务器,vite build 构建生产包,vite preview 在本地预览生产构建结果。

1.3 入口文件与 index.html 的特殊地位

与 Webpack 以 JavaScript 为入口不同,Vite 将项目根目录的 index.html 作为入口文件。Vite 会解析 HTML 中的 <script type="module" src="..."> 标签,将其指向的 JavaScript 文件作为应用入口。

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

这一设计意味着 HTML 在 Vite 中是一等公民——它决定了整个模块图的起点。Vite 会分析 HTML 中引用的所有资源(JS、CSS、图片等),然后按需编译。


2. 开发服务器与依赖预构建

2.1 冷启动速度:为什么 Vite 这么快?

传统打包工具(Webpack、Parcel)在开发模式下需要完整打包整个应用,包括所有路由和依赖,然后才能启动开发服务器。随着项目规模增长,打包时间线性增加。Vite 采用了完全不同的策略:

  1. 依赖预构建:使用 esbuild 将 CommonJS/UMD 模块预先转换为 ESM,并将多个内部模块合并为一个(减少浏览器请求数)。esbuild 用 Go 语言编写,比 JavaScript 编写的打包器快 10-100 倍。
  2. 源码按需编译:Vite 将应用源码划分为两类——依赖node_modules 中的库)和源码(开发者编写的代码)。依赖在首次启动时预构建并缓存(node_modules/.vite 目录),源码则在浏览器请求时才通过 esbuild 实时编译为 ESM。
1
2
3
4
5
6
7
8
9
10
// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
// 指定预构建的依赖(Vite 通常自动检测,但当包不符合 ESM 标准时需手动指定)
optimizeDeps: {
include: ['lodash-es'],
exclude: [] // 排除某些依赖不进行预构建
}
});

2.2 HMR(热模块替换)原理

Vite 的 HMR 建立在浏览器原生 ES Modules 之上,不需要像 Webpack 那样通过打包后的 bundle 来追踪模块依赖图。在 Vite 中,每个文件都是一个独立的模块,浏览器通过 type="module" 直接加载。

HMR 工作流程

  1. 文件变更:Vite 的文件监听器检测到文件修改。
  2. 模块失效:Vite 向浏览器发送 WebSocket 消息,告知哪些模块已更新。消息中携带精确的模块路径。
  3. 浏览器重新请求:浏览器丢弃已更新的模块缓存,向 Vite 开发服务器重新请求该模块。由于 ES Modules 的依赖图在浏览器端是原生维护的,Vite 只需重新编译被修改的文件及其直接依赖边界。
  4. 模块替换:新模块被加载后,框架的 HMR 插件(如 @vitejs/plugin-vue)执行相应的替换逻辑(更新组件状态、触发 re-render)。

这与 Webpack 的 HMR 有本质区别:Webpack 需要从入口模块开始,沿打包后的模块依赖图找到哪些 chunk 受影响,然后重新构建整个 chunk 并推送。Vite 利用浏览器原生模块系统,仅重新编译被修改的文件,从而使得 HMR 速度与项目规模解耦——无论应用多大,更新速度始终保持在毫秒级。

WebSocket 通信示例(浏览器端简化逻辑):

1
2
3
4
5
6
7
8
9
10
// Vite 注入到客户端的 HMR 代码(简化示意)
const socket = new WebSocket(`ws://localhost:5173`);

socket.addEventListener('message', ({ data }) => {
const { type, path } = JSON.parse(data);
if (type === 'update') {
// 删除浏览器模块缓存
import.meta.hot?.accept(); // 通知 Vite 客户端重新请求
}
});

3. 插件机制

Vite 的插件系统建立在 Rollup 插件接口 之上,同时扩展了Vite 专有钩子,用于处理开发服务器特有的任务(如预构建优化、HMR 配置、HTML 转换等)。这使得大多数 Rollup 插件可以直接在 Vite 中使用。

3.1 Rollup 兼容插件

Vite 兼容绝大多数 Rollup 插件——只需在 vite.config.tsplugins 数组中导入并注册即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import legacy from '@vitejs/plugin-legacy';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
plugins: [
vue(), // Vite 官方插件
legacy({
targets: ['defaults', 'not IE 11']
}),
visualizer() // Rollup 插件
]
});

3.2 Vite 专有钩子

Vite 额外提供了一些 Rollup 没有的钩子,用于处理开发服务器的特有需求:

钩子 阶段 用途
config 配置解析前 修改用户传入的配置(合并默认值)。
configResolved 配置解析后 读取最终解析的配置,常用于缓存配置供后续使用。
configureServer 开发服务器启动前 添加自定义中间件、自定义路由、注册 HMR 事件。
configurePreviewServer 预览服务器启动前 configureServer 类似,作用于 vite preview
transformIndexHtml HTML 转换 修改 index.html 内容(注入脚本、标签)。
handleHotUpdate HMR 更新 自定义 HMR 更新逻辑,返回受影响的模块列表。

configureServer 钩子示例:为开发服务器添加一个 API 代理中间件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
plugins: [
{
name: 'custom-middleware',
configureServer(server) {
// 添加自定义中间件
server.middlewares.use('/api', (req, res, next) => {
if (req.url === '/api/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok' }));
return;
}
next();
});
}
}
]
});

3.3 unplugin 统一插件方案

unplugin 是一个工具库,允许你编写一次插件,同时兼容 Vite、Webpack、Rollup、esbuild 等多个构建工具。许多流行的 Vite 插件(如 unplugin-auto-importunplugin-vue-components)都是基于 unplugin 构建的。它们本质上解析了各构建工具的插件接口差异,向上提供统一的 API。


4. 构建配置的实战示例

一个典型的 Vite 配置文件,集成了路径别名、CSS 模块、代理转发和分包策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';

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

resolve: {
alias: {
'@': resolve(__dirname, 'src'), // 路径别名
'@components': resolve(__dirname, 'src/components')
}
},

css: {
modules: {
localsConvention: 'camelCaseOnly' // CSS Modules 命名规则
},
preprocessorOptions: {
scss: {
additionalData: '@import "@/styles/variables.scss";' // 全局注入 SCSS
}
}
},

server: {
port: 3000,
proxy: {
'/api': 'http://localhost:8080' // API 代理
}
},

build: {
rollupOptions: {
output: {
manualChunks: { // 分包策略
vendor: ['vue', 'vue-router'],
ui: ['element-plus']
}
}
}
}
});

课后练习

一、概念自测(选择题 / 填空题)

  1. (单选) Vite 在生产构建时使用什么引擎进行打包?
    A. esbuild
    B. Webpack
    C. Rollup
    D. swc

  2. (单选) Vite 的 HMR 为什么在大型项目中仍然保持极快的更新速度?
    A. 它使用 WebSocket 批量推送所有变更文件。
    B. 它利用浏览器原生 ES Modules,仅重新编译被修改的文件,速度与项目规模解耦。
    C. 它通过缓存所有编译结果避免重新计算。
    D. 它依赖 Node.js 的多线程并行编译。

  3. (填空) Vite 使用 ______ 将 CommonJS 依赖预先转换为 ESM,利用其 Go 语言实现的高性能加速冷启动。

  4. (多选) 以下哪些是 Vite 专有钩子(Rollup 中没有)?
    A. configureServer
    B. transform
    C. transformIndexHtml
    D. handleHotUpdate

二、AI 编程任务:编写面向 AI 的提示词

场景:你需要从零初始化一个 Vue 3 + TypeScript 的 Vite 项目。要求如下:

  • 使用 create-vite 模板初始化项目。
  • 配置 @ 路径别名指向 src 目录。
  • 配置开发服务器端口为 4000,并设置 /api 代理到 http://localhost:3000
  • 安装并配置 @vitejs/plugin-vue(Vite 官方 Vue 插件)。
  • build.rollupOptions 中配置 manualChunks,将 vuevue-router 拆分为单独的 vendor chunk。
  • 生成完整的 vite.config.tspackage.json 文件。

任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成符合上述要求的配置文件。提示词中需明确指定 Vite 配置项、插件导入和路径别名。

三、Agent 模式下的提示词示例

你是一个资深前端开发 Agent。请创建一个 Vue 3 + TypeScript 的 Vite 项目骨架。需要生成以下文件:

  1. package.json:包含 vuevue-router 为 dependencies,vite@vitejs/plugin-vuetypescript 为 devDependencies。scripts 包含 dev: "vite"build: "vue-tsc --noEmit && vite build"preview: "vite preview"
  2. vite.config.ts:使用 defineConfig 配置。引入 @vitejs/plugin-vueresolve.alias 设置 @ 指向 ./srcserver.port4000server.proxy/api 转发到 http://localhost:3000build.rollupOptions.output.manualChunks 中将 vuevue-router 拆分为 vendor chunk。
  3. index.html:标准的 Vite 入口 HTML,<script type="module" src="/src/main.ts">
  4. src/main.ts:导入 createApp,创建 Vue 应用并挂载。
  5. tsconfig.json:设置 compilerOptionstarget: "ES2022"module: "ESNext"moduleResolution: "bundler"strict: trueinclude: ["src"]
    所有文件添加简要注释,确保可运行的完整骨架。完成后列出所有文件内容。

四、面试真题与参考答案

题目(字节跳动前端面试题):

请解释 Vite 与 Webpack 在开发模式下的核心差异。为什么 Vite 能做到“无论项目多大,HMR 都保持毫秒级更新”?它的依赖预构建解决了什么问题?

参考答案

Vite 与 Webpack 在开发模式下的根本区别在于模块加载方式。Webpack 在开发模式下也需要完整打包整个应用的依赖图,将源码和依赖一起编译、合并为 bundle chunks,然后启动开发服务器。随着项目规模增大,打包时间线性增长,HMR 需要重新打包受影响的 chunk。

Vite 利用浏览器原生 ES Modules 能力——在开发环境下,源码文件作为独立的 ESM 模块直接从开发服务器按需提供给浏览器,无需事先打包。Vite 只在浏览器请求某个模块时才使用 esbuild 进行实时编译。这样 HMR 时只需重新编译被修改的单个文件(以及其直接依赖边界),而无需处理整个 chunk 图,因此更新速度与项目规模解耦。

Vite 的依赖预构建解决的是两个问题:一是 node_modules 中大量依赖使用 CommonJS 格式,浏览器无法直接导入——esbuild 将它们转换为 ESM 并合并为少量请求(减少瀑布式加载);二是将多个内部小模块合并为一个,大幅减少浏览器的网络请求数,避免“加载瀑布”导致的冷启动慢。


课后练习答案

一、概念自测答案

  1. C

    • 解析:生产构建使用 Rollup 进行打包优化。esbuild 用于开发环境的依赖预构建和实时编译。
  2. B

    • 解析:Vite HMR 利用浏览器原生 ESM,仅重新编译被修改的文件,速度与项目大小解耦。A 错误,Vite 通过 WebSocket 推送变更消息但仅针对修改的文件;C 和 D 不是核心原因。
  3. esbuild

    • 解析:esbuild 是 Go 语言编写的高性能编译器,Vite 用它进行依赖预构建和源码实时编译。
  4. A、C、D

    • 解析:configureServertransformIndexHtmlhandleHotUpdate 是 Vite 专有钩子。transform 是 Rollup 通用钩子,Vite 也支持。

二、AI 编程任务参考答案(提示词示例)

示例提示词
“请生成一个 Vue 3 + TypeScript 的 Vite 项目配置。要求:

  • package.json:包含 vuevue-router(dependencies),vite@vitejs/plugin-vuetypescript(devDependencies)。scripts 包含 devbuild(含 vue-tsc --noEmit)、preview
  • vite.config.ts:使用 defineConfig。引入 @vitejs/plugin-vue。设置 @ 别名指向 srcserver.port: 4000server.proxy: { '/api': 'http://localhost:3000' }build.rollupOptions.output.manualChunks: { vendor: ['vue', 'vue-router'] }
  • tsconfig.json:严格模式,target: ES2022module: ESNextmoduleResolution: bundlerinclude: ['src']
  • 直接输出所有文件的完整内容。”
CATALOG
  1. 1. 第78课:Vite 构建工具入门——项目初始化、开发服务器、HMR 原理、插件机制
    1. 1.1. 1. Vite 的设计哲学与项目初始化
      1. 1.1.1. 1.1 Vite 的两大核心组件
      2. 1.1.2. 1.2 创建 Vite 项目
      3. 1.1.3. 1.3 入口文件与 index.html 的特殊地位
    2. 1.2. 2. 开发服务器与依赖预构建
      1. 1.2.1. 2.1 冷启动速度:为什么 Vite 这么快?
      2. 1.2.2. 2.2 HMR(热模块替换)原理
    3. 1.3. 3. 插件机制
      1. 1.3.1. 3.1 Rollup 兼容插件
      2. 1.3.2. 3.2 Vite 专有钩子
      3. 1.3.3. 3.3 unplugin 统一插件方案
    4. 1.4. 4. 构建配置的实战示例
    5. 1.5. 课后练习
      1. 1.5.1. 一、概念自测(选择题 / 填空题)
      2. 1.5.2. 二、AI 编程任务:编写面向 AI 的提示词
      3. 1.5.3. 三、Agent 模式下的提示词示例
      4. 1.5.4. 四、面试真题与参考答案
    6. 1.6. 课后练习答案
      1. 1.6.1. 一、概念自测答案
      2. 1.6.2. 二、AI 编程任务参考答案(提示词示例)