Appearance
Vite 源代码导览
项目结构
vite/
├── packages/
│ ├── vite/ # 核心包
│ │ ├── src/
│ │ │ ├── node/ # Node.js 特定代码
│ │ │ │ ├── server/ # 开发服务器
│ │ │ │ │ ├── index.ts # 服务器入口
│ │ │ │ │ ├── middlewares.ts # 中间件
│ │ │ │ │ ├── moduleGraph.ts # 模块图
│ │ │ │ │ └── pluginContainer.ts # 插件容器
│ │ │ │ ├── optimizer/ # 依赖优化器
│ │ │ │ │ ├── index.ts # 优化器入口
│ │ │ │ │ ├── esbuildDepPlugin.ts # esbuild 插件
│ │ │ │ │ └── scan.ts # 依赖扫描
│ │ │ │ └── build/ # 构建器
│ │ │ │ ├── index.ts # 构建入口
│ │ │ │ └── plugins.ts # 构建插件
│ │ │ ├── client/ # 客户端代码
│ │ │ │ ├── client.ts # 客户端入口
│ │ │ │ ├── env.ts # 环境变量
│ │ │ │ └── overlay.ts # 错误覆盖层
│ │ │ ├── shared/ # 共享代码
│ │ │ │ ├── constants.ts # 常量
│ │ │ │ ├── utils.ts # 工具函数
│ │ │ │ └── config.ts # 配置
│ │ │ ├── plugins/ # 内置插件
│ │ │ │ ├── index.ts # 插件入口
│ │ │ │ ├── css.ts # CSS 插件
│ │ │ │ ├── esbuild.ts # esbuild 插件
│ │ │ │ ├── json.ts # JSON 插件
│ │ │ │ └── asset.ts # 资源插件
│ │ │ └── index.ts # 主入口
│ │ └── package.json
│ ├── plugin-react/ # React 插件
│ └── plugin-vue/ # Vue 插件
├── playground/ # 测试项目
├── scripts/ # 构建脚本
└── package.json核心文件详解
1. 开发服务器入口
文件路径: packages/vite/src/node/server/index.ts
功能: 创建和配置 Vite 开发服务器
关键代码:
typescript
// 创建开发服务器
export async function createServer(
inlineConfig: InlineConfig = {}
): Promise<ViteDevServer> {
// 1. 解析配置
const config = await resolveConfig(inlineConfig, 'serve')
// 2. 创建 HTTP 服务器
const server = await createHttpServer(config)
// 3. 创建中间件
const middlewares = createMiddlewares(config)
// 4. 创建模块图
const moduleGraph = new ModuleGraph()
// 5. 创建插件容器
const pluginContainer = new PluginContainer(config)
// 6. 创建 WebSocket 服务器
const ws = createWebSocketServer(config)
// 7. 返回服务器实例
return {
config,
server,
middlewares,
moduleGraph,
pluginContainer,
ws,
// ... 其他方法
}
}
// 创建 HTTP 服务器
async function createHttpServer(
config: ResolvedConfig
): Promise<http.Server> {
const { serverConfig } = config
const middlewareMode = !!serverConfig.middlewareMode
const server = http.createServer((req, res) => {
// 处理请求
middlewares(req, res)
})
return server
}设计决策:
- 使用 Node.js 原生
http模块创建服务器 - 通过中间件模式处理请求
- 分离关注点:服务器、中间件、模块图、插件容器
2. 中间件系统
文件路径: packages/vite/src/node/server/middlewares.ts
功能: 处理不同类型的请求
关键代码:
typescript
// 创建中间件
export function createMiddlewares(
config: ResolvedConfig
): Connect.Server {
const middlewares = connect()
// 1. 基础中间件
middlewares.use(compression())
middlewares.use(urlencoded())
middlewares.use(json())
// 2. 开发服务器中间件
middlewares.use(baseMiddleware(config))
middlewares.use('/', indexHtmlMiddleware(config))
middlewares.use('/@fs/', servePublicMiddleware(config))
middlewares.use('/@vite/', serveStaticMiddleware(config))
// 3. 转换中间件
middlewares.use(transformMiddleware(config))
// 4. HMR 中间件
middlewares.use(hmrMiddleware(config))
// 5. 代理中间件
if (config.server.proxy) {
middlewares.use(proxyMiddleware(config))
}
return middlewares
}
// 转换中间件
function transformMiddleware(config: ResolvedConfig) {
return async (req: IncomingMessage, res: ServerResponse, next: NextFunction) => {
const url = req.url!
// 1. 检查是否需要转换
if (!isTransformRequest(url)) {
return next()
}
// 2. 读取文件
const file = await resolveFile(url, config)
// 3. 应用插件转换
const result = await pluginContainer.transform(file, url)
// 4. 返回转换结果
res.setHeader('Content-Type', 'application/javascript')
res.end(result.code)
}
}设计决策:
- 使用
connect库作为中间件基础 - 按顺序应用中间件
- 每个中间件专注于特定功能
3. 模块图
文件路径: packages/vite/src/node/server/moduleGraph.ts
功能: 追踪模块依赖关系,管理 HMR
关键代码:
typescript
// 模块图类
export class ModuleGraph {
private urlToModuleMap = new Map<string, ModuleNode>()
private idToModuleMap = new Map<string, ModuleNode>()
private fileToModulesMap = new Map<string, Set<ModuleNode>>()
// 获取模块
getModuleByUrl(url: string): ModuleNode | undefined {
return this.urlToModuleMap.get(url)
}
// 创建模块
async ensureEntryFromUrl(url: string): Promise<ModuleNode> {
const mod = this.urlToModuleMap.get(url)
if (mod) {
return mod
}
const module = new ModuleNode(url)
this.urlToModuleMap.set(url, module)
// 解析模块
await this.resolveModule(module)
return module
}
// 解析模块
private async resolveModule(module: ModuleNode) {
// 1. 读取文件
const source = await fs.readFile(module.url, 'utf-8')
// 2. 解析导入
const imports = parseImports(source)
// 3. 解析依赖
for (const imp of imports) {
const dep = await this.ensureEntryFromUrl(imp)
module.imports.add(dep)
dep.importers.add(module)
}
}
// 更新模块(HMR)
async updateModule(file: string) {
const modules = this.fileToModulesMap.get(file)
if (!modules) return
// 1. 重新转换模块
for (const mod of modules) {
await this.transformModule(mod)
}
// 2. 触发 HMR
await this.triggerHMR(modules)
}
// 触发 HMR
private async triggerHMR(modules: Set<ModuleNode>) {
const updates: Update[] = []
for (const mod of modules) {
const update: Update = {
type: 'js-update',
timestamp: Date.now(),
path: mod.url,
acceptedPath: mod.url
}
updates.push(update)
}
// 发送 HMR 更新
this.ws.send({
type: 'update',
updates
})
}
}
// 模块节点类
class ModuleNode {
url: string
id: string
imports: Set<ModuleNode>
importers: Set<ModuleNode>
transformResult: TransformResult | null
lastHMRTimestamp: number
constructor(url: string) {
this.url = url
this.id = generateId(url)
this.imports = new Set()
this.importers = new Set()
this.transformResult = null
this.lastHMRTimestamp = 0
}
}设计决策:
- 使用 Map 数据结构快速查找模块
- 双向追踪:imports 和 importers
- 支持增量更新
4. 依赖优化器
文件路径: packages/vite/src/node/optimizer/index.ts
功能: 使用 esbuild 预构建依赖
关键代码:
typescript
// 依赖优化器类
export class DepOptimizer {
private config: ResolvedConfig
private cacheDir: string
private metadataPath: string
constructor(config: ResolvedConfig) {
this.config = config
this.cacheDir = path.join(config.root, 'node_modules/.vite')
this.metadataPath = path.join(this.cacheDir, '_metadata.json')
}
// 扫描依赖
async scanDeps(): Promise<Record<string, string>> {
const entry = this.config.root
const result = await esbuild.context({
entryPoints: [entry],
bundle: true,
write: false,
plugins: [esbuildDepPlugin(this.config)]
})
const deps: Record<string, string> = {}
// 解析依赖
for (const file of result.outputFiles) {
const imports = parseImports(file.text)
for (const imp of imports) {
if (isDep(imp)) {
deps[imp] = resolveDep(imp)
}
}
}
return deps
}
// 预构建依赖
async preBundle(deps: Record<string, string>) {
const entryPoints = Object.keys(deps)
const result = await esbuild.build({
entryPoints,
bundle: true,
format: 'esm',
write: true,
outdir: this.cacheDir,
plugins: [esbuildDepPlugin(this.config)]
})
// 保存元数据
await this.saveMetadata(deps)
return result
}
// 检查缓存
async checkCache(): Promise<boolean> {
if (!fs.existsSync(this.metadataPath)) {
return false
}
const metadata = JSON.parse(fs.readFileSync(this.metadataPath, 'utf-8'))
const currentHash = await this.computeHash()
return metadata.hash === currentHash
}
// 计算哈希
private async computeHash(): Promise<string> {
const lockFile = path.join(this.config.root, 'package-lock.json')
const content = fs.readFileSync(lockFile, 'utf-8')
return createHash('sha256').update(content).digest('hex')
}
// 保存元数据
private async saveMetadata(deps: Record<string, string>) {
const hash = await this.computeHash()
const metadata = {
hash,
deps,
timestamp: Date.now()
}
fs.writeFileSync(this.metadataPath, JSON.stringify(metadata, null, 2))
}
}设计决策:
- 使用 esbuild 进行快速预构建
- 基于文件哈希的缓存策略
- 元数据持久化
5. 插件容器
文件路径: packages/vite/src/node/server/pluginContainer.ts
功能: 管理插件,执行插件钩子
关键代码:
typescript
// 插件容器类
export class PluginContainer {
private plugins: Plugin[]
private hooks: Map<string, Function[]>
constructor(config: ResolvedConfig) {
this.plugins = config.plugins
this.hooks = new Map()
// 收集插件钩子
this.collectHooks()
}
// 收集插件钩子
private collectHooks() {
for (const plugin of this.plugins) {
// 配置钩子
if (plugin.config) {
this.addHook('config', plugin.config)
}
// 解析钩子
if (plugin.resolveId) {
this.addHook('resolveId', plugin.resolveId)
}
// 加载钩子
if (plugin.load) {
this.addHook('load', plugin.load)
}
// 转换钩子
if (plugin.transform) {
this.addHook('transform', plugin.transform)
}
}
}
// 添加钩子
private addHook(name: string, hook: Function) {
if (!this.hooks.has(name)) {
this.hooks.set(name, [])
}
this.hooks.get(name)!.push(hook)
}
// 执行钩子
async runHook(name: string, ...args: any[]): Promise<any> {
const hooks = this.hooks.get(name)
if (!hooks) return
for (const hook of hooks) {
const result = await hook(...args)
if (result !== undefined) {
return result
}
}
}
// 解析模块 ID
async resolveId(
id: string,
importer: string
): Promise<ResolveIdResult | null> {
return await this.runHook('resolveId', id, importer)
}
// 加载模块
async load(id: string): Promise<LoadResult | null> {
return await this.runHook('load', id)
}
// 转换模块
async transform(
code: string,
id: string
): Promise<TransformResult | null> {
return await this.runHook('transform', code, id)
}
}设计决策:
- 钩子按顺序执行
- 第一个返回非 undefined 的结果被使用
- 支持异步钩子
6. CSS 插件
文件路径: packages/vite/src/plugins/css.ts
功能: 处理 CSS 文件
关键代码:
typescript
// CSS 插件
export function cssPlugin(): Plugin {
return {
name: 'vite:css',
// 转换 CSS
async transform(code, id) {
if (!id.endsWith('.css')) {
return null
}
// 1. 处理 @import
const imports = parseImports(code)
for (const imp of imports) {
code = code.replace(imp, `import '${imp}'`)
}
// 2. 处理 url()
const urls = parseUrls(code)
for (const url of urls) {
const resolved = resolveUrl(url)
code = code.replace(url, resolved)
}
// 3. 返回转换结果
return {
code: `import { updateStyle } from '/@vite/client'\n` +
`const css = ${JSON.stringify(code)}\n` +
`updateStyle('${id}', css)\n`,
map: null
}
}
}
}
// 解析 CSS 导入
function parseImports(code: string): string[] {
const imports: string[] = []
const regex = /@import\s+['"]([^'"]+)['"]/g
let match
while ((match = regex.exec(code)) !== null) {
imports.push(match[1])
}
return imports
}
// 解析 CSS URL
function parseUrls(code: string): string[] {
const urls: string[] = []
const regex = /url\(['"]([^'"]+)['"]\)/g
let match
while ((match = regex.exec(code)) !== null) {
urls.push(match[1])
}
return urls
}设计决策:
- 将 CSS 转换为 JavaScript
- 使用客户端运行时注入样式
- 支持热更新
7. esbuild 插件
文件路径: packages/vite/src/plugins/esbuild.ts
功能: 使用 esbuild 转换 TypeScript 和 JSX
关键代码:
typescript
// esbuild 插件
export function esbuildPlugin(config: ResolvedConfig): Plugin {
return {
name: 'vite:esbuild',
// 转换代码
async transform(code, id) {
// 1. 检查是否需要转换
if (!isTransformable(id)) {
return null
}
// 2. 使用 esbuild 转换
const result = await esbuild.transform(code, {
loader: getLoader(id),
target: config.build.target,
sourcemap: true
})
// 3. 返回转换结果
return {
code: result.code,
map: result.map
}
}
}
}
// 获取加载器
function getLoader(id: string): esbuild.Loader {
if (id.endsWith('.ts')) return 'ts'
if (id.endsWith('.tsx')) return 'tsx'
if (id.endsWith('.jsx')) return 'jsx'
if (id.endsWith('.js')) return 'js'
return 'js'
}
// 检查是否可转换
function isTransformable(id: string): boolean {
return /\.(ts|tsx|jsx|js)$/.test(id)
}设计决策:
- 使用 esbuild 进行快速转换
- 自动检测文件类型
- 生成 source map
8. 客户端代码
文件路径: packages/vite/src/client/client.ts
功能: 客户端运行时,处理 HMR
关键代码:
typescript
// 客户端入口
async function initClient() {
// 1. 创建 WebSocket 连接
const ws = new WebSocket(`ws://${location.host}`)
// 2. 监听 HMR 更新
ws.addEventListener('message', async (event) => {
const payload = JSON.parse(event.data)
if (payload.type === 'update') {
await handleHMRUpdate(payload.updates)
}
})
// 3. 监听错误
ws.addEventListener('error', (error) => {
console.error('WebSocket error:', error)
})
}
// 处理 HMR 更新
async function handleHMRUpdate(updates: Update[]) {
for (const update of updates) {
if (update.type === 'js-update') {
await handleJSUpdate(update)
}
}
}
// 处理 JS 更新
async function handleJSUpdate(update: JSUpdate) {
const { path, acceptedPath, timestamp } = update
// 1. 重新导入模块
const mod = await import(path + `?t=${timestamp}`)
// 2. 调用 HMR 接受
if (mod.__hmrAccept) {
await mod.__hmrAccept()
}
// 3. 通知客户端
console.log(`[HMR] ${path} updated`)
}
// 更新样式
export function updateStyle(id: string, css: string) {
const style = document.getElementById(id) as HTMLStyleElement
if (style) {
style.textContent = css
} else {
const newStyle = document.createElement('style')
newStyle.id = id
newStyle.textContent = css
document.head.appendChild(newStyle)
}
}
// 启动客户端
initClient()设计决策:
- 使用 WebSocket 进行实时通信
- 支持模块级热更新
- 样式热更新
关键算法
1. 依赖解析算法
typescript
// 递归解析依赖
async function resolveDeps(
entry: string,
seen: Set<string> = new Set()
): Promise<string[]> {
if (seen.has(entry)) {
return []
}
seen.add(entry)
const deps: string[] = [entry]
// 1. 读取文件
const code = await fs.readFile(entry, 'utf-8')
// 2. 解析导入
const imports = parseImports(code)
// 3. 递归解析依赖
for (const imp of imports) {
const resolved = await resolveId(imp, entry)
if (resolved) {
const subDeps = await resolveDeps(resolved, seen)
deps.push(...subDeps)
}
}
return deps
}2. HMR 算法
typescript
// 触发 HMR
async function triggerHMR(file: string) {
// 1. 找到受影响的模块
const modules = findAffectedModules(file)
// 2. 检查模块是否接受 HMR
const accepted = modules.filter(mod => mod.acceptsHMR)
if (accepted.length === 0) {
// 整页刷新
sendFullReload()
return
}
// 3. 更新接受的模块
for (const mod of accepted) {
await updateModule(mod)
}
// 4. 发送 HMR 更新
sendHMRUpdate(accepted)
}
// 找到受影响的模块
function findAffectedModules(file: string): ModuleNode[] {
const modules: ModuleNode[] = []
const visited = new Set<string>()
// BFS 遍历
const queue = [file]
while (queue.length > 0) {
const current = queue.shift()!
if (visited.has(current)) continue
visited.add(current)
const mod = moduleGraph.getModuleByUrl(current)
if (mod) {
modules.push(mod)
// 添加导入者
for (const importer of mod.importers) {
queue.push(importer.url)
}
}
}
return modules
}3. 缓存算法
typescript
// 缓存管理器
class CacheManager {
private cache: Map<string, CacheEntry>
constructor() {
this.cache = new Map()
}
// 获取缓存
get(key: string): any {
const entry = this.cache.get(key)
if (!entry) return null
// 检查是否过期
if (Date.now() > entry.expires) {
this.cache.delete(key)
return null
}
return entry.value
}
// 设置缓存
set(key: string, value: any, ttl: number = 60000) {
this.cache.set(key, {
value,
expires: Date.now() + ttl
})
}
// 清除缓存
clear() {
this.cache.clear()
}
// 清除过期缓存
clearExpired() {
const now = Date.now()
for (const [key, entry] of this.cache.entries()) {
if (now > entry.expires) {
this.cache.delete(key)
}
}
}
}
interface CacheEntry {
value: any
expires: number
}总结
Vite 的源代码结构清晰,模块化程度高。核心功能包括:
- 开发服务器: 处理 HTTP 请求,提供 HMR
- 中间件系统: 处理不同类型的请求
- 模块图: 追踪模块依赖关系
- 依赖优化器: 使用 esbuild 预构建依赖
- 插件系统: 扩展 Vite 功能
- 内置插件: 处理 CSS、TypeScript、JSX 等
- 客户端运行时: 处理 HMR 和样式注入
通过理解这些核心文件和算法,可以深入掌握 Vite 的工作原理。

扫描二维码关注"架构师AI杜"公众号,获取更多技术内容和最新动态
