Vite 从零搭建项目——超越 Webpack 的构建利器
如果你读过本博客之前的文章《从入门到入土使用 Webpack5 搭建一个项目》,你会知道配置 Webpack 是一件相当繁琐的事情。而 Vite 的出现,让开发体验有了质的飞跃——启动速度从几十秒降至几百毫秒,热更新几乎感觉不到延迟。
本文将先讲清楚 Vite 为什么快,再带你从零搭建一个完整的 Vite + React + TypeScript 项目。
Vite 为什么这么快?
Webpack 的问题:先打包再启动
Webpack 在启动开发服务器时,需要从入口文件出发,递归分析所有模块依赖,将所有模块打包成 bundle,然后才启动服务器。项目越大,模块越多,启动时间越长。
Webpack 启动流程:
入口文件 → 分析依赖 → 编译所有模块 → 打包成 bundle → 启动服务器
Vite 的解法:按需编译,直接利用 ESM
Vite 的开发服务器不做预打包。它利用现代浏览器原生支持的 ES Modules(ESM),在浏览器请求某个模块时才实时编译,实现真正的按需加载。
Vite 启动流程:
启动服务器(毫秒级)→ 浏览器请求文件 → Vite 编译该文件 → 返回给浏览器
整个流程中,Vite 利用了两个关键工具:
-
esbuild:用 Go 语言编写,编译速度比 Babel 快 10~100 倍,用于依赖预构建(将
node_modules中的 CommonJS 模块转换为 ESM)。 -
Rollup:用于生产环境构建,产出优化良好的静态资源。
热更新(HMR)的差异
- Webpack HMR:模块变化时,需要重新构建整个依赖链,项目越大越慢
- Vite HMR:只处理变化的模块本身,响应时间恒定,不受项目规模影响
Vite vs Webpack 核心对比
| 维度 | Webpack | Vite |
|---|---|---|
| 冷启动 | 慢(打包所有模块) | 极快(按需编译) |
| HMR | 随项目增大变慢 | 恒定快速 |
| 配置复杂度 | 高 | 低(合理的默认值) |
| 生态成熟度 | 非常成熟 | 快速成长 |
| 构建工具 | Webpack + Babel | esbuild(开发)+ Rollup(生产) |
| 适用场景 | 老项目、特殊需求 | 新项目首选 |
从零搭建 Vite + React + TypeScript 项目
第一步:初始化项目
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
Vite 官方提供了多种模板,支持 Vue、React、Svelte、Lit 等框架,也有带 TypeScript 的版本(react-ts、vue-ts)。
启动后,你会看到终端输出:
VITE v5.x.x ready in 300 ms ← 注意这个速度
➜ Local: http://localhost:5173/
第二步:了解项目结构
my-app/
├── public/ # 静态资源(不会被 Vite 处理)
├── src/
│ ├── assets/ # 会被 Vite 处理的资源(图片等)
│ ├── App.tsx
│ ├── main.tsx # 入口文件
│ └── vite-env.d.ts
├── index.html # ⚠️ 入口 HTML 在根目录,不是 public/
├── vite.config.ts # Vite 配置文件
├── tsconfig.json
└── package.json
注意:与 Webpack 不同,Vite 的入口 index.html 在根目录,因为 Vite 以 HTML 为入口直接服务文件。
第三步:配置路径别名
避免 ../../components/Button 这样的相对路径,用 @ 替代 src/:
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
同时更新 tsconfig.json 让 TypeScript 也能识别:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
现在可以这样写了:
import { Button } from "@/components/Button";
import { useAuth } from "@/hooks/useAuth";
第四步:配置环境变量
Vite 使用 .env 文件管理环境变量,变量名必须以 VITE_ 开头才能暴露给客户端代码:
# .env.development(开发环境)
VITE_API_BASE_URL=http://localhost:3000/api
VITE_APP_TITLE=My App (Dev)
# .env.production(生产环境)
VITE_API_BASE_URL=https://api.example.com
VITE_APP_TITLE=My App
在代码中使用:
// 访问环境变量
const apiUrl = import.meta.env.VITE_API_BASE_URL;
const isDev = import.meta.env.DEV; // 内置变量
const isProd = import.meta.env.PROD; // 内置变量
const mode = import.meta.env.MODE; // 'development' | 'production'
为了有 TypeScript 类型提示,在 src/vite-env.d.ts 中声明:
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string;
readonly VITE_APP_TITLE: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
第五步:常用插件配置
npm install -D @vitejs/plugin-react unplugin-auto-import unplugin-icons
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import AutoImport from "unplugin-auto-import/vite";
import path from "path";
export default defineConfig({
plugins: [
react(),
// 自动导入 React Hooks,无需在每个文件手动 import
AutoImport({
imports: ["react", "react-router-dom"],
dts: "src/auto-imports.d.ts", // 生成类型声明文件
}),
],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
server: {
port: 3000,
// 配置代理,解决开发环境跨域
proxy: {
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
});
第六步:生产构建优化
// vite.config.ts
export default defineConfig({
build: {
// 代码分割:将 node_modules 单独打包
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
router: ["react-router-dom"],
},
},
},
// 启用 gzip 压缩报告
reportCompressedSize: true,
// chunk 超过 500KB 时警告
chunkSizeWarningLimit: 500,
},
});
运行构建并分析产物:
npm run build # 构建
npm run preview # 本地预览构建结果
完整的 vite.config.ts 参考
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
return {
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
server: {
port: 3000,
open: true, // 自动打开浏览器
proxy: {
"/api": {
target: env.VITE_API_BASE_URL,
changeOrigin: true,
},
},
},
build: {
outDir: "dist",
sourcemap: mode === "development",
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
},
// 静态资源文件名加 hash,利于缓存
chunkFileNames: "js/[name]-[hash].js",
assetFileNames: "assets/[name]-[hash][extname]",
},
},
},
css: {
// 开启 CSS Modules
modules: {
localsConvention: "camelCase",
},
},
};
});
从 Webpack 迁移到 Vite 的注意事项
如果你正在考虑从 Webpack 项目迁移到 Vite,有几点需要注意:
require()不被支持:Vite 基于 ESM,需要将require()改为importprocess.env变为import.meta.env:环境变量的访问方式不同__dirname需要手动定义:ESM 中没有__dirname,需要用import.meta.url替代- CommonJS 库需要预构建:Vite 会自动处理,但某些复杂情况需要在
optimizeDeps中配置
// 替代 __dirname
import { fileURLToPath } from "url";
const __dirname = fileURLToPath(new URL(".", import.meta.url));
总结
Vite 之所以能超越 Webpack,根本原因在于它改变了开发服务器的工作模式——从”先全量打包再服务”变成”按需编译直接服务”。这个架构上的差异,在大型项目中会带来数量级的体验提升。
新项目应该优先选择 Vite,而老项目可以评估迁移成本后逐步切换。配合本文的配置参考,你可以快速搭建起一个规范的生产级项目。