Vite 简介与核心理念

Vite(法语意为”快速”)是由 Vue.js 作者尤雨溪开发的下一代前端构建工具。它基于原生 ES 模块和现代浏览器特性,在开发环境中使用 esbuild 提供极快的冷启动和热更新体验,在生产环境中使用 Rollup 进行优化构建。

Vite 的革命性设计理念

Vite 代表了前端构建工具的范式转换,它摒弃了传统的”打包优先”思路,转而采用”按需编译”的策略。这种设计哲学的核心在于充分利用现代浏览器的原生 ES 模块支持和高性能的 esbuild 工具。

传统构建工具的痛点

  • 冷启动慢:需要预先打包整个应用
  • 热更新慢:修改文件后需要重新打包相关模块
  • 开发体验差:等待时间长,影响开发效率

Vite 的解决方案

  • 即时启动:开发服务器无需预打包,秒级启动
  • 精确热更新:只更新变化的模块,毫秒级响应
  • 原生 ES 模块:直接利用浏览器的模块加载能力

Vite 双引擎架构原理

开发引擎 (esbuild)

  • 目标:极致的开发体验
  • 特点:速度优先,功能够用即可
  • 技术:Go 语言编写,原生性能

生产引擎 (Rollup)

  • 目标:优化的生产构建
  • 特点:质量优先,输出最优代码
  • 技术:成熟的插件生态,强大的优化能力

Vite 核心运行流程详解

开发环境运行流程

1. 服务启动阶段

  • 依赖预构建:扫描项目依赖,使用 esbuild 预构建第三方库
  • 服务器启动:启动基于 Connect 的开发服务器
  • 路由注册:注册各种中间件处理不同类型的请求

2. 模块请求处理

  • 路径解析:将浏览器请求的路径解析为实际的文件路径
  • 文件转换:根据文件类型选择相应的转换器
  • 依赖重写:将裸导入重写为可访问的路径
  • 响应返回:将转换后的内容返回给浏览器

3. 热更新流程

  • 文件监听:监听项目文件的变化
  • 影响分析:分析文件变化对模块图的影响
  • 精确更新:只更新受影响的模块
  • 状态保持:尽可能保持应用的运行时状态

生产环境构建流程

1. 预构建阶段

  • 依赖分析:分析所有的模块依赖关系
  • 代码分割:根据配置和启发式规则进行代码分割
  • 资源收集:收集所有需要处理的静态资源

2. Rollup 构建阶段

  • 模块解析:使用 Rollup 解析模块依赖
  • Tree Shaking:移除未使用的代码
  • 代码优化:进行各种代码优化和压缩
  • 资源处理:处理图片、字体等静态资源

3. 输出生成阶段

  • 文件生成:生成最终的构建文件
  • Hash 计算:为文件添加内容哈希
  • Manifest 生成:生成资源映射表
  • 部署准备:整理输出目录结构

双引擎协调机制

配置统一

  • 同一套配置文件同时控制开发和生产行为
  • 插件系统确保转换逻辑的一致性
  • 环境变量和构建选项的统一管理

插件兼容

  • Vite 插件既支持开发时的实时转换,也支持生产时的批量处理
  • Rollup 插件可以直接在 Vite 中使用
  • 自动适配不同环境的插件行为

路径解析一致性

  • 开发和生产使用相同的路径解析逻辑
  • 别名配置在两个环境中保持一致
  • 资源引用方式统一处理

双引擎设计

下面的代码展示了 Vite 如何在配置层面协调两个引擎:

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
// vite.config.js - 双引擎配置示例
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

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

// 开发环境配置 (esbuild)
esbuild: {
target: "es2020",
format: "esm",
platform: "browser",
},

// 生产环境配置 (Rollup)
build: {
target: "es2015",
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
utils: ["lodash", "date-fns"],
},
},
},
},
});

模块解析机制

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// Vite 模块解析流程
class ViteModuleResolver {
constructor(config) {
this.config = config;
this.moduleCache = new Map();
this.dependencyGraph = new Map();
}

async resolveModule(id, importer) {
// 1. 检查缓存
if (this.moduleCache.has(id)) {
return this.moduleCache.get(id);
}

// 2. 路径解析
const resolved = await this.resolvePath(id, importer);

// 3. 依赖预构建检查
if (this.needsPreBuild(resolved)) {
return this.preBuildDependency(resolved);
}

// 4. 转换处理
const transformed = await this.transformModule(resolved);

// 5. 缓存结果
this.moduleCache.set(id, transformed);

return transformed;
}

resolvePath(id, importer) {
// 相对路径解析
if (id.startsWith("./") || id.startsWith("../")) {
return path.resolve(path.dirname(importer), id);
}

// 绝对路径解析
if (id.startsWith("/")) {
return path.resolve(this.config.root, id.slice(1));
}

// node_modules 解析
return this.resolveNodeModule(id);
}

needsPreBuild(modulePath) {
// 判断是否需要预构建
return modulePath.includes("node_modules") && !modulePath.endsWith(".js");
}

async transformModule(modulePath) {
const code = await fs.readFile(modulePath, "utf-8");

// TypeScript 转换
if (modulePath.endsWith(".ts") || modulePath.endsWith(".tsx")) {
return this.transformTypeScript(code, modulePath);
}

// Vue SFC 转换
if (modulePath.endsWith(".vue")) {
return this.transformVue(code, modulePath);
}

// React JSX 转换
if (modulePath.endsWith(".jsx")) {
return this.transformJSX(code, modulePath);
}

return code;
}
}

Vite 与 Rollup 的深度集成

Rollup 在 Vite 中的角色

Vite 在生产环境中完全基于 Rollup 进行构建,这种设计带来了以下优势:

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
// vite.config.js - Rollup 配置集成
export default defineConfig({
build: {
// Rollup 配置选项
rollupOptions: {
// 输入配置
input: {
main: path.resolve(__dirname, "index.html"),
admin: path.resolve(__dirname, "admin.html"),
},

// 外部依赖
external: ["react", "react-dom"],

// 输出配置
output: {
// 手动分包
manualChunks: (id) => {
if (id.includes("node_modules")) {
if (id.includes("react")) {
return "react-vendor";
}
if (id.includes("antd")) {
return "ui-vendor";
}
return "vendor";
}
},

// 自定义文件名
entryFileNames: "assets/[name]-[hash].js",
chunkFileNames: "assets/[name]-[hash].js",
assetFileNames: "assets/[name]-[hash].[ext]",
},

// Rollup 插件
plugins: [
// 可以直接使用 Rollup 插件
],
},
},
});

Vite 插件与 Rollup 插件的兼容性

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
// 插件兼容性示例
import { defineConfig } from "vite";
import { resolve } from "path";

// Rollup 插件在 Vite 中的使用
import commonjs from "@rollup/plugin-commonjs";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";

// Vite 专用插件
import vue from "@vitejs/plugin-vue";
import react from "@vitejs/plugin-react";

export default defineConfig({
plugins: [
// Vite 专用插件
vue(),
react(),

// Rollup 插件 (需要特殊处理)
{
...commonjs(),
apply: "build", // 只在构建时应用
},

{
...nodeResolve(),
apply: "build",
},

// 条件应用插件
process.env.NODE_ENV === "production" && typescript(),
].filter(Boolean),
});

开发与生产环境的差异处理

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
45
46
47
48
49
50
51
52
53
// 环境差异处理机制
class ViteEnvironmentHandler {
constructor() {
this.isDev = process.env.NODE_ENV === "development";
this.isProd = process.env.NODE_ENV === "production";
}

// 开发环境:ES 模块直接服务
async serveDevelopment(moduleId) {
const module = await this.loadModule(moduleId);

// 直接返回 ES 模块
return {
code: module.code,
map: module.map,
headers: {
"Content-Type": "application/javascript",
"Cache-Control": "no-cache",
},
};
}

// 生产环境:Rollup 构建
async buildProduction(config) {
const rollupConfig = this.generateRollupConfig(config);

// 使用 Rollup 进行构建
const bundle = await rollup.rollup(rollupConfig);

// 生成优化后的文件
await bundle.generate({
format: "es",
dir: "dist",
...config.build.rollupOptions.output,
});

return bundle;
}

generateRollupConfig(viteConfig) {
return {
input: viteConfig.build.rollupOptions.input,
external: viteConfig.build.rollupOptions.external,
plugins: [
// 转换 Vite 插件为 Rollup 插件
...this.transformVitePlugins(viteConfig.plugins),

// 添加 Rollup 特定插件
...this.getRollupSpecificPlugins(),
],
};
}
}

快速开始与基础配置

项目初始化

1
2
3
4
5
6
7
8
# 使用 npm
npm create vite@latest my-project

# 使用 yarn
yarn create vite my-project

# 使用 pnpm
pnpm create vite my-project

基础配置文件

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// vite.config.js - 基础配置
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { resolve } from "path";

export default defineConfig({
// 插件配置
plugins: [vue()],

// 开发服务器配置
server: {
port: 3000,
open: true,
cors: true,
proxy: {
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},

// 构建配置
build: {
outDir: "dist",
sourcemap: true,
minify: "terser",
rollupOptions: {
output: {
manualChunks: {
vue: ["vue"],
vendor: ["lodash", "axios"],
},
},
},
},

// 路径解析
resolve: {
alias: {
"@": resolve(__dirname, "src"),
"@components": resolve(__dirname, "src/components"),
"@utils": resolve(__dirname, "src/utils"),
},
},

// CSS 配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`,
},
},
modules: {
localsConvention: "camelCase",
},
},

// 环境变量
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
__BUILD_TIME__: JSON.stringify(new Date().toISOString()),
},
});

开发服务器深度解析

模块请求处理流程

当浏览器请求一个模块时,Vite 开发服务器会经历以下处理步骤:

  1. URL 解析:解析请求的 URL,确定对应的文件路径
  2. 文件读取:从文件系统读取原始文件内容
  3. 插件转换:应用相应的插件进行代码转换
  4. 依赖重写:重写 import 语句,使其指向正确的模块
  5. 缓存处理:缓存转换结果,提高后续请求性能
  6. 响应返回:将处理后的内容返回给浏览器

依赖重写机制

Vite 开发服务器的一个关键功能是依赖重写,它解决了浏览器无法直接解析 Node.js 风格的裸导入的问题:

1
2
3
4
5
6
7
// 原始代码
import React from "react";
import { lodash } from "lodash";

// Vite 重写后
import React from "/@fs/node_modules/react/index.js";
import { lodash } from "/@fs/node_modules/lodash/index.js";

这种重写机制的优势:

  • 兼容性:保持与现有代码的完全兼容
  • 透明性:开发者无需修改任何代码
  • 灵活性:支持各种复杂的导入场景

开发服务器架构

下面的代码展示了 Vite 开发服务器的核心架构实现:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Vite 开发服务器核心实现
class ViteDevServer {
constructor(config) {
this.config = config;
this.app = connect(); // 基于 Connect 中间件
this.server = null;
this.moduleGraph = new ModuleGraph();
this.pluginContainer = new PluginContainer(config.plugins);
}

async listen(port = 3000) {
// 设置中间件
this.setupMiddlewares();

// 启动服务器
this.server = this.app.listen(port, () => {
console.log(`Dev server running at http://localhost:${port}`);
});

return this.server;
}

setupMiddlewares() {
// 1. CORS 支持
this.app.use(cors());

// 2. 静态资源服务
this.app.use("/public", serveStatic(this.config.publicDir));

// 3. 模块转换中间件
this.app.use(this.transformMiddleware.bind(this));

// 4. HMR WebSocket
this.app.use(this.hmrMiddleware.bind(this));

// 5. HTML 重写中间件
this.app.use(this.htmlRewriteMiddleware.bind(this));

// 6. 代理中间件
if (this.config.server.proxy) {
this.setupProxy();
}
}

async transformMiddleware(req, res, next) {
const url = req.url;

// 跳过非 JS 模块
if (!this.shouldTransform(url)) {
return next();
}

try {
// 解析模块
const module = await this.resolveModule(url);

// 转换代码
const transformed = await this.transformModule(module);

// 设置响应头
res.setHeader("Content-Type", "application/javascript");
res.setHeader("Cache-Control", "no-cache");

// 返回转换后的代码
res.end(transformed.code);
} catch (error) {
this.handleError(error, req, res);
}
}

async transformModule(module) {
// 1. 插件转换链
let result = { code: module.code, map: null };

for (const plugin of this.pluginContainer.plugins) {
if (plugin.transform) {
result = await plugin.transform(result.code, module.id);
}
}

// 2. Import 分析和重写
result.code = this.rewriteImports(result.code, module.id);

// 3. HMR 注入
if (this.config.server.hmr) {
result.code = this.injectHMR(result.code, module.id);
}

return result;
}

rewriteImports(code, moduleId) {
// 重写裸导入为完整路径
return code.replace(
/import\s+(.+)\s+from\s+['"]([^'"]+)['"]/g,
(match, imports, source) => {
if (this.isBareImport(source)) {
// 转换为预构建的依赖路径
const resolved = this.resolvePreBuiltDep(source);
return `import ${imports} from '${resolved}'`;
}
return match;
}
);
}
}

生产构建与优化

Vite 在生产环境中完全转换为基于 Rollup 的构建策略,这种设计确保了开发和生产环境的最佳平衡:开发时追求速度,生产时追求质量。

生产构建的设计原理

为什么生产环境选择 Rollup?

  1. 成熟的优化策略

    • Rollup 在代码优化方面经验丰富
    • 原生支持 Tree Shaking
    • 优秀的作用域提升能力
  2. 丰富的插件生态

    • 大量成熟的 Rollup 插件可直接使用
    • 社区经验积累深厚
    • 插件质量普遍较高
  3. 输出质量优先

    • 生成的代码体积小
    • 运行时性能好
    • 兼容性处理完善

生产构建流程详解

1. 构建准备阶段

  • 配置合并:将 Vite 配置转换为 Rollup 配置
  • 插件适配:将 Vite 插件转换为 Rollup 插件
  • 依赖解析:分析所有的模块依赖关系

2. 代码分析阶段

  • 入口点识别:确定所有的构建入口
  • 依赖图构建:建立完整的模块依赖图
  • 分割策略制定:决定如何进行代码分割

3. 代码转换阶段

  • TypeScript 编译:将 TypeScript 转换为 JavaScript
  • JSX 转换:处理 React/Vue 等框架的语法
  • CSS 处理:处理样式文件,包括预处理器

4. 优化处理阶段

  • Tree Shaking:移除未使用的代码
  • 代码压缩:使用 Terser 等工具压缩代码
  • 资源优化:压缩图片、字体等静态资源

5. 输出生成阶段

  • 文件生成:生成最终的构建文件
  • Hash 添加:为文件添加内容哈希用于缓存
  • Manifest 生成:生成资源映射表

Vite 的构建优化策略

智能代码分割

  • 自动识别第三方依赖并分离
  • 根据路由进行动态分割
  • 提取公共模块避免重复

资源处理优化

  • 小图片自动转换为 Base64
  • 字体文件智能压缩
  • CSS 提取和压缩

缓存优化

  • 文件内容哈希确保缓存有效性
  • 合理的分包策略提高缓存命中率
  • 支持浏览器缓存和 CDN 缓存

插件系统与生态

官方插件生态

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
45
46
47
// vite.config.js - 官方插件使用
import { defineConfig } from "vite";

// 框架插件
import vue from "@vitejs/plugin-vue";
import react from "@vitejs/plugin-react";
import preact from "@vitejs/plugin-preact";
import svelte from "@vitejs/plugin-svelte";

// 功能插件
import legacy from "@vitejs/plugin-legacy";
import { resolve } from "path";

export default defineConfig({
plugins: [
// Vue 支持
vue({
include: [/\.vue$/, /\.md$/], // 支持 markdown 作为 Vue 组件
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith("my-"),
},
},
}),

// React 支持
react({
include: "**/*.{jsx,tsx}",
babel: {
plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
},
}),

// 传统浏览器支持
legacy({
targets: ["defaults", "not IE 11"],
}),

// PWA 支持
VitePWA({
registerType: "autoUpdate",
workbox: {
globPatterns: ["**/*.{js,css,html,ico,png,svg}"],
},
}),
],
});

社区插件示例

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
45
46
47
48
49
50
51
// 常用社区插件配置
import { defineConfig } from "vite";
import { createHtmlPlugin } from "vite-plugin-html";
import { visualizer } from "rollup-plugin-visualizer";
import viteCompression from "vite-plugin-compression";
import { viteStaticCopy } from "vite-plugin-static-copy";

export default defineConfig({
plugins: [
// HTML 模板处理
createHtmlPlugin({
minify: true,
inject: {
data: {
title: "My Vite App",
description: "A modern web application",
},
},
}),

// 构建分析
visualizer({
filename: "dist/stats.html",
open: true,
gzipSize: true,
}),

// Gzip 压缩
viteCompression({
algorithm: "gzip",
ext: ".gz",
}),

// 文件复制
viteStaticCopy({
targets: [
{
src: "assets/robots.txt",
dest: ".",
},
],
}),

// Mock 数据
viteMockServe({
mockPath: "mock",
localEnabled: true,
prodEnabled: false,
}),
],
});

模块热替换 (HMR) 原理

HMR 架构设计

Vite 的 HMR 系统采用了客户端-服务端协作的架构:

服务端职责

  • 文件监听:监控项目文件的变化
  • 依赖分析:分析文件变化对模块图的影响
  • 更新决策:决定哪些模块需要更新
  • 通知推送:通过 WebSocket 推送更新信息

客户端职责

  • 连接维护:维持与服务端的 WebSocket 连接
  • 更新接收:接收服务端的更新通知
  • 模块替换:执行实际的模块替换逻辑
  • 状态恢复:尽可能恢复或保持应用状态

HMR 更新策略

1. 精确影响分析

  • Vite 维护了一个完整的模块依赖图
  • 当文件发生变化时,分析其对依赖图的影响
  • 只标记真正受影响的模块进行更新

2. 边界检测

  • 检测更新边界,确定更新的范围
  • 如果模块接受自身更新,则在该模块停止传播
  • 如果模块不接受更新,则继续向上传播

3. 状态保持策略

  • 框架级别的状态保持(如 React Fast Refresh)
  • 应用级别的状态保持(通过 HMR API)
  • 自动状态恢复机制

HMR API 设计

Vite 提供了灵活的 HMR API,允许开发者精确控制模块的热更新行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// HMR API 使用示例
if (import.meta.hot) {
// 接受自身更新
import.meta.hot.accept((newModule) => {
// 更新逻辑
});

// 接受依赖更新
import.meta.hot.accept(["./dependency.js"], ([newDep]) => {
// 依赖更新逻辑
});

// 模块卸载时的清理
import.meta.hot.dispose(() => {
// 清理逻辑
});
}

框架集成的 HMR

不同框架的 HMR 集成策略各有特色:

React Fast Refresh

  • 保持组件状态
  • 支持钩子函数的更新
  • 错误边界处理

Vue HMR

  • 支持模板、脚本、样式的独立更新
  • 保持组件实例状态
  • 支持 Composition API

Svelte HMR

  • 支持组件的热更新
  • 保持本地状态
  • 支持样式更新

依赖预构建机制

依赖预构建是 Vite 架构中的一个关键组成部分,它解决了 ES 模块和 CommonJS 模块之间的兼容性问题,同时显著提升了开发服务器的性能。

依赖预构建的必要性

为什么需要预构建?

  1. 模块格式兼容性

    • 大多数 npm 包仍然使用 CommonJS 格式
    • 浏览器原生只支持 ES 模块
    • 需要转换 CommonJS 为 ES 模块
  2. 性能优化

    • 减少 HTTP 请求数量(将多个文件合并)
    • 缓存第三方依赖(避免重复处理)
    • 提高模块加载速度
  3. 兼容性处理

    • 处理不同的导入导出语法
    • 解决循环依赖问题
    • 统一模块路径格式

预构建的工作流程

1. 依赖发现阶段

  • 扫描项目入口文件和 HTML 文件
  • 识别所有的裸导入(bare imports)
  • 收集需要预构建的依赖列表

2. 依赖分析阶段

  • 分析每个依赖的 package.json
  • 确定依赖的入口文件
  • 检测依赖的模块格式

3. 预构建执行阶段

  • 使用 esbuild 进行快速转换
  • 生成 ES 模块格式的代码
  • 创建依赖映射表

4. 缓存管理阶段

  • 存储预构建结果到缓存目录
  • 生成缓存元数据
  • 检测依赖变化以决定是否重新构建

esbuild 在预构建中的作用

Vite 选择 esbuild 进行依赖预构建的原因:

性能优势

  • Go 语言编写,原生性能
  • 并行处理,充分利用多核 CPU
  • 相比传统工具快 10-100 倍

功能完备

  • 支持 TypeScript、JSX 转换
  • 内置 CommonJS 到 ES 模块的转换
  • 支持代码分割和树摇

稳定可靠

  • 成熟的工具,bug 少
  • 与 Vite 的需求完美匹配
  • 活跃的社区支持

预构建缓存策略

Vite 采用了智能的缓存策略来优化预构建性能:

缓存触发条件

  • package.json 的 dependencies 发生变化
  • 包管理器的锁文件发生变化
  • Vite 配置文件中的相关配置发生变化

缓存失效机制

  • 自动检测依赖版本变化
  • 支持手动清除缓存
  • 开发时支持强制重新构建

预构建配置优化

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
// vite.config.js - 预构建优化配置
export default defineConfig({
optimizeDeps: {
// 强制包含的依赖
include: ["react", "react-dom", "lodash-es", "@ant-design/icons"],

// 排除的依赖
exclude: ["your-local-package", "@some/esm-package"],

// esbuild 选项
esbuildOptions: {
target: "es2020",
plugins: [
// 自定义 esbuild 插件
{
name: "custom-resolver",
setup(build) {
build.onResolve({ filter: /^custom:/ }, (args) => {
return {
path: args.path,
namespace: "custom",
};
});
},
},
],
},

// 强制重新预构建
force: process.env.FORCE_OPTIMIZE === "true",
},
});

性能优化策略

开发环境优化

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
// vite.config.js - 开发环境优化
export default defineConfig({
// 预构建优化
optimizeDeps: {
// 包含大型库
include: ["react", "react-dom", "lodash-es", "date-fns", "antd"],

// 强制预构建
force: process.env.FORCE_OPTIMIZE === "true",
},

// 服务器优化
server: {
// 预热文件
warmup: {
clientFiles: ["./src/components/*.vue", "./src/utils/*.js"],
},

// 源码映射
sourcemapIgnoreList: (sourcePath, sourcemapPath) => {
return sourcePath.includes("node_modules");
},
},

// esbuild 优化
esbuild: {
// 目标版本
target: "es2020",

// 去除调试信息
drop: process.env.NODE_ENV === "production" ? ["console", "debugger"] : [],
},
});

生产环境优化

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// vite.config.js - 生产环境优化
export default defineConfig({
build: {
// 构建目标
target: "es2015",

// 文件大小限制
chunkSizeWarningLimit: 500,

// 内联资源限制
assetsInlineLimit: 4096,

// CSS 代码分割
cssCodeSplit: true,

// Rollup 优化
rollupOptions: {
output: {
// 手动分包
manualChunks: (id) => {
// 第三方库分包策略
if (id.includes("node_modules")) {
// React 生态
if (id.includes("react") || id.includes("react-dom")) {
return "react-vendor";
}

// 图表库
if (id.includes("echarts") || id.includes("d3")) {
return "charts-vendor";
}

// UI 库
if (id.includes("antd") || id.includes("@ant-design")) {
return "ui-vendor";
}

// 工具库
if (id.includes("lodash") || id.includes("moment")) {
return "utils-vendor";
}

// 其他第三方库
return "vendor";
}

// 业务代码分包
if (id.includes("src/pages/")) {
const pageName = id.split("src/pages/")[1].split("/")[0];
return `page-${pageName}`;
}
},
},
},

// Terser 压缩配置
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ["console.log", "console.info"],
passes: 2,
},
mangle: {
safari10: true,
},
format: {
comments: false,
safari10: true,
},
},
},
});

缓存策略优化

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// build/cache-optimizer.js - 缓存优化
export class ViteCacheOptimizer {
constructor(config) {
this.config = config;
this.cacheStrategies = new Map();
}

setupCacheStrategies() {
return {
name: "cache-optimizer",

generateBundle(opts, bundle) {
Object.keys(bundle).forEach((fileName) => {
const chunk = bundle[fileName];

if (chunk.type === "chunk") {
// 分析代码稳定性
const stability = this.analyzeStability(chunk);

// 根据稳定性设置文件名
if (stability === "stable") {
// 稳定代码使用内容哈希
chunk.fileName = chunk.fileName.replace(
"[hash]",
this.generateContentHash(chunk.code)
);
} else {
// 不稳定代码使用时间戳
chunk.fileName = chunk.fileName.replace(
"[hash]",
Date.now().toString(36)
);
}
}
});
},
};
}

analyzeStability(chunk) {
// 分析代码稳定性
const modules = Object.keys(chunk.modules);

// 第三方库代码认为是稳定的
const vendorModules = modules.filter((id) => id.includes("node_modules"));

// 如果主要是第三方库代码,认为是稳定的
if (vendorModules.length / modules.length > 0.8) {
return "stable";
}

return "unstable";
}

generateContentHash(content) {
return crypto.createHash("md5").update(content).digest("hex").slice(0, 8);
}
}