This repository has been archived on 2026-05-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
obsidian/DEV/VITE/架构原理.md
T
2026-04-20 22:47:51 +08:00

21 KiB

tags, create time
tags create time
DEV
VITE
architecture
internals
2026-04-18

Vite 架构原理

核心设计理念

Vite 的核心创新在于利用浏览器原生能力和分层优化策略,将开发环境和生产环境采用完全不同的处理方式。

graph TB
    A[用户请求] --> B{环境判断}
    B -->|开发环境| C[Dev Server]
    B -->|生产环境| D[Pre-built Assets]
    C --> E[ESM 按需编译]
    E --> F[即时返回]
    D --> G[Rollup 优化产物]
    G --> H[CDN/HTTP 服务器]

开发环境架构

服务启动流程

sequenceDiagram
    participant CLI as 用户命令行
    participant Config as 配置加载器
    participant Server as Dev Server
    participant Plugin as 插件系统
    participant Monitor as 文件监听器

    CLI->>Config: 读取配置文件
    Config->>Server: 创建服务器实例
    Server->>Plugin: 注册所有插件
    Plugin->>Server: 返回钩子函数
    Server->>Monitor: 启动文件监听
    Monitor->>Server: 建立连接
    Server-->>CLI: 服务器启动完成

HTTP 请求处理流程

graph TD
    A[HTTP 请求] --> B[中间件层]
    B --> C{请求类型判断}
    C -->|HTML 文件| D[响应 HTML]
    C -->|JS/TS 文件| E[transform 链]
    C -->|CSS 文件| F[CSS 处理器]
    C -->|静态资源| G[资源处理]
    E --> H[Plugin 转换]
    H --> I[返回编译后代码]
    F --> I
    G --> J[返回资源引用]

请求处理实现

class ViteDevServer {
  private pluginContainer: PluginContainer
  private fileWatcher: FSWatcher
  private moduleGraph: ModuleGraph

  async handleRequest(req: IncomingMessage, res: ServerResponse) {
    const url = req.url!

    try {
      // 1. 处理 HTML 入口文件
      if (url.endsWith('.html')) {
        await this.handleHtmlRequest(url, res)
        return
      }

      // 2. 处理模块请求
      if (this.isModuleRequest(url)) {
        const module = await this.transformModule(url)
        this.sendModuleResponse(module, res)
        return
      }

      // 3. 处理静态资源
      const asset = await this.resolveAsset(url)
      if (asset) {
        this.sendAssetResponse(asset, res)
        return
      }
    } catch (error) {
      this.handleError(error, res)
    }
  }
}

模块系统实现

模块图结构

graph LR
    A[main.tsx] --> B[App.tsx]
    A --> C[router.tsx]
    B --> D[Header.tsx]
    B --> E[Counter.tsx]
    C --> F[Pages.tsx]
    E --> G[useState.ts]
    E --> H[useEffect.ts]

模块节点实现

class ModuleNode {
  public readonly id: string
  public url: string
  public file: string

  // 依赖关系
  public importers = new Set<ModuleNode>()
  public importedModules = new Set<ModuleNode>()
  public importedBindings: Record<string, string[]> = {}

  // 转换结果
  public transformResult: TransformResult | null = null
  public lastHMRTimestamp = 0

  // SSR 信息
  public ssrModule: any = null
  public ssrTransformResult: TransformResult | null = null

  constructor(id: string) {
    this.id = id
    this.url = id
    this.file = path.normalize(id)
  }
}

class ModuleGraph {
  private modules = new Map<string, ModuleNode>()
  private fileToModulesMap = new Map<string, Set<ModuleNode>>()

  async ensureEntryFromUrl(url: string): Promise<ModuleNode> {
    const resolved = await this.resolveUrl(url)
    return this.ensureModule(resolved.id, resolved.url)
  }

  async ensureModule(
    id: string,
    url: string,
    ssr?: boolean
  ): Promise<ModuleNode> {
    let module = this.modules.get(id)
    
    if (!module) {
      module = new ModuleNode(id)
      this.modules.set(id, module)
      
      // 建立文件映射
      const file = path.resolve(id)
      let fileModules = this.fileToModulesMap.get(file)
      if (!fileModules) {
        fileModules = new Set()
        this.fileToModulesMap.set(file, fileModules)
      }
      fileModules.add(module)
    }
    
    return module
  }

  updateModule(module: ModuleNode, transformed: TransformResult) {
    module.transformResult = transformed
    module.lastHMRTimestamp = Date.now()
  }

  /**
   * 失效模块及其导入者
   */
  invalidateModule(mod: ModuleNode): void {
    mod.transformResult = null
    mod.ssrModule = null
    mod.ssrTransformResult = null

    // 级联失效所有导入者
    const invalidators = new Set<ModuleNode>()
    const queue: ModuleNode[] = [...mod.importers]

    while (queue.length > 0) {
      const importer = queue.pop()!
      invalidators.add(importer)

      // 如果导入者的代码需要重新编译
      if (!importer.transformResult) {
        importer.importers.forEach(dep => {
          if (!invalidators.has(dep)) {
            queue.push(dep)
          }
        })
      }
    }
  }

  /**
   * 检测循环依赖
   */
  hasCircularDependency(module: ModuleNode): boolean {
    const visited = new Set<string>()
    const stack = [module.id]

    while (stack.length > 0) {
      const currentId = stack.pop()!

      if (visited.has(currentId)) {
        console.warn(`[vite] Circular dependency detected: ${currentId}`)
        return true
      }

      visited.add(currentId)
      const currentModule = this.modules.get(currentId)
      
      if (currentModule) {
        currentModule.importedModules.forEach(dep => {
          if (!visited.has(dep.id)) {
            stack.push(dep.id)
          }
        })
      }
    }

    return false
  }
}

ES Module 编译器

源码转换流水线

graph TD
    A[原始源代码] --> B[Plugin Transform 钩子]
    B --> C[语法转换]
    C --> D[依赖注入]
    D --> E[包装 ESM]
    E --> F[生成 Source Map]
    F --> G[返回处理后的代码]

依赖预构建

class DepsOptimizer {
  private depsCacheDir: string
  private scanner: DepsOptimizer

  async scanImports(): Promise<ScanResult> {
    const entries = await this.getEntryPoints()
    
    const discovered = await esbuild.context({
      entryPoints: entries,
      bundle: true,
      write: false,
      onEnd: (result) => {
        const dependencies = this.extractDependencies(result.metafile)
        this.updateOptimizedDeps(dependencies)
      }
    })

    return discovered
  }

  async optimizeDeps(deps: Record<string, string>): Promise<void> {
    const optimizedDeps = new Map<string, string>()

    for (const [id, file] of Object.entries(deps)) {
      try {
        // 转换为 ESM
        const result = await esbuild.build({
          entryPoints: [file],
          bundle: true,
          format: 'esm',
          target: 'esnext',
          write: false,
          packages: 'external'
        })

        optimizedDeps.set(id, file)
        
        // 缓存到 disk
        await this.saveOptimizedDep(id, result)
      } catch (e) {
        console.error(`Failed to optimize dependency: ${id}`, e)
      }
    }
  }

  async saveOptimizedDep(id: string, result: BuildResult): Promise<void> {
    const outputPath = path.join(this.depsCacheDir, `${id}.js`)
    const metaPath = path.join(this.depsCacheDir, `${id}.js.meta.json`)

    await fs.writeFile(
      outputPath,
      result.outputFiles[0].text,
      'utf-8'
    )

    await fs.writeFile(
      metaPath,
      JSON.stringify({
        file: id,
        src: path.basename(outputPath)
      }),
      'utf-8'
    )
  }
}

热模块替换 (HMR)

HMR 工作机制

sequenceDiagram
    participant File as 文件系统
    participant Watcher as 监听器
    participant Server as Dev Server
    participant WS as WebSocket
    participant Client as 浏览器客户端

    File->>Watcher: 文件变更
    Watcher->>Server: 触发 change 事件
    Server->>Server: 失效相关模块
    Server->>Server: 重新编译变更模块
    Server->>WS: 发送 HMR 更新
    WS->>Client: 接收更新通知
    Client->>Client: 执行模块替换
    Client->>Server: 发送确认消息

HMR 协议

// 服务端发送的 HMR 消息类型
interface HMRCustomEvent {
  type: 'custom'
  event: string
  data: any
}

interface HMRUpdateEvent {
  type: 'update'
  updates: Array<{
    type: 'js-update' | 'css-update' | 'static-update'
    path: string
    acceptedPath: string
    timestamp: number
  }>
}

interface HMRConnectedEvent {
  type: 'connected'
}

interface HMRPruneEvent {
  type: 'prune'
  paths: string[]
}

// 客户端处理实现
class HMRClient {
  private ws: WebSocket
  private pendingImports = new Map<string, Promise<void>>()

  async handleUpdate(updates: HMRUpdateEvent['updates']) {
    for (const update of updates) {
      switch (update.type) {
        case 'js-update':
          await this.handleJSModuleUpdate(update)
          break
        case 'css-update':
          await this.handleCSSUpdate(update)
          break
        case 'static-update':
          this.handleStaticUpdate(update)
          break
      }
    }
  }

  private async handleJSModuleUpdate(update: Extract<HMRUpdateEvent['updates'][0], { type: 'js-update' }>) {
    const { path, timestamp, acceptedPath } = update

    // 使用浏览器原生 import API 重新导入模块
    const importPromise = import(`${path}?t=${timestamp}`).then((mod) => {
      // 查找模块的热替换处理器
      const modUrl = acceptedPath || path
      const modObject = window.__vite_module_cache__[modUrl]
      
      if (modObject && modObject.hot) {
        // 触发热替换处理器
        modObject.hot.accept(mod)
        
        // 执行清理回调
        if (modObject.hot._dispose) {
          modObject.hot._dispose()
        }
      }
    })

    this.pendingImports.set(path, importPromise)
  }
}

组件级 HMR

// React 组件的 HMR 实现
import { createHot } from '@vitejs/plugin-react/client'

// 在开发环境中,Vite 会自动注入
if (import.meta.hot) {
  import.meta.hot.accept('./App.tsx', (newModule) => {
    // React Fast Refresh 处理
    const prevComponent = window.__vite_plugin_react_component__
    window.__vite_plugin_react_component__ = newModule.default
    
    // 触发组件重新渲染
    window.__vite_plugin_react_rerender__()
  })
}

插件系统架构

插件生命周期

graph TD
    A[Config 阶段] --> B[ConfigResolved]
    B --> C[ConfigureServer]
    C --> D[BuildStart]
    D --> E[Transform Phase]
    E --> F[Load Phase]
    F --> G[BuildEnd]
    G --> H[CloseBundle]

插件容器实现

class PluginContainer {
  private plugins: Plugin[]
  private hooks: Record<string, Function[]>

  constructor(plugins: Plugin[]) {
    this.plugins = plugins
    this.hooks = {}
    this.registerHooks(plugins)
  }

  private registerHooks(plugins: Plugin[]) {
    const hookNames: validHooks[] = [
      'buildStart', 'buildEnd',
      'resolveId', 'load', 'transform',
      'configureServer'
    ]

    hookNames.forEach(hookName => {
      this.hooks[hookName] = plugins
        .map(plugin => plugin[hookName])
        .filter(Boolean)
    })
  }

  async resolveId(id: string, importer?: string): Promise<string | null> {
    const resolveHooks = this.hooks['resolveId']
    
    for (const hook of resolveHooks) {
      const result = await hook.call(this, id, importer)
      
      if (result) {
        return result
      }
    }
    
    return null
  }

  async load(id: string): Promise<string | null> {
    const loadHooks = this.hooks['load']
    
    for (const hook of loadHooks) {
      const result = await hook.call(this, id)
      
      if (result) {
        return result
      }
    }
    
    return null
  }

  async transform(code: string, id: string): Promise<TransformResult | null> {
    const transformHooks = this.hooks['transform']
    let result = { code, map: null }

    for (const hook of transformHooks) {
      const transformed = await hook.call(this, result.code, id)
      
      if (transformed) {
        result = {
          code: transformed.code,
          map: transformed.map || result.map
        }
      }
    }
    
    return result.code !== code ? result : null
  }
}

构建系统 (生产环境)

Rollup 集成

graph TD
    A[入口文件] --> B[依赖图构建]
    B --> C[模块解析]
    C --> D[代码转换]
    D --> E[代码分割]
    E --> F[Tree Shaking]
    F --> G[minification]
    G --> H[输出生成]

构建流程实现

class BuildSystem {
  async build(config: ResolvedConfig): Promise<BuildResult> {
    // 1. 分析入口文件
    const entries = this.resolveEntries(config)
    
    // 2. 构建模块图
    const moduleGraph = await this.buildModuleGraph(entries)
    
    // 3. 代码优化
    const optimizedModules = await this.optimizeModules(moduleGraph)
    
    // 4. 代码分割
    const chunks = this.splitChunks(optimizedModules)
    
    // 5. 压缩混淆
    const minifyResults = await this.minifyChunks(chunks)
    
    // 6. 生成输出
    const outputs = this.generateOutputs(minifyResults)
    
    return { modules: outputs, warnings: [], errors: [] }
  }

  private async optimizeModules(moduleGraph: ModuleGraph): Promise<ModuleNode[]> {
    const modules = Array.from(moduleGraph.modules.values())
    
    // 应用插件转换
    for (const module of modules) {
      if (module.transformResult) {
        const result = await this.pluginContainer.transform(
          module.transformResult.code,
          module.id
        )
        
        if (result) {
          module.transformResult = result
        }
      }
    }
    
    return modules
  }

  private splitChunks(modules: ModuleNode[]): Chunk[] {
    const chunks: Map<string, Chunk> = new Map()
    
    // 识别共享依赖
    const sharedDeps = this.findSharedDependencies(modules)
    
    // 按配置生成代码分割
    const manualChunksConfig = {
      'vendor-react': ['react', 'react-dom'],
      'vendor-ui': ['antd']
    }
    
    Object.entries(manualChunksConfig).forEach(([chunkName, depNames]) => {
      const chunkModules = modules.filter(mod =>
        depNames.some(dep => mod.id.includes(dep))
      )
      
      chunks.set(chunkName, {
        name: chunkName,
        modules: chunkModules,
        fileName: `${chunkName}.js`
      })
    })
    
    return Array.from(chunks.values())
  }

  private async minifyChunks(chunks: Chunk[]): Promise<MinifyResult[]> {
    const results: MinifyResult[] = []
    
    for (const chunk of chunks) {
      const code = this.generateChunkCode(chunk)
      
      // 使用 terser 进行压缩
      const minified = await terser.minify(code, {
        compress: {
          drop_console: true,
          pure_funcs: ['console.log', 'console.info']
        },
        mangle: {
          toplevel: true,
          properties: {
            regex: /^_/
          }
        }
      })
      
      if (minified.code) {
        results.push({
          chunkName: chunk.name,
          code: minified.code,
          map: minified.map
        })
      }
    }
    
    return results
  }
}

性能优化机制

缓存策略

graph TD
    A[请求模块] --> B{缓存检查}
    B -->|内存缓存命中| C[返回缓存]
    B -->|磁盘缓存命中| D[加载并返回]
    B -->|缓存未命中| E[编译模块]
    E --> F[写入缓存]
    F --> C

多层缓存实现

class CacheSystem {
  private memoryCache = new Map<string, CacheEntry>()
  private diskCache: DiskCache
  private etagCache = new Map<string, string>()

  async get(key: string): Promise<string | null> {
    // 1. 检查内存缓存
    const memEntry = this.memoryCache.get(key)
    if (memEntry && !this.isExpired(memEntry)) {
      return memEntry.content
    }

    // 2. 检查磁盘缓存
    try {
      const diskEntry = await this.diskCache.get(key)
      if (diskEntry) {
        // 写入内存缓存
        this.memoryCache.set(key, {
          content: diskEntry.content,
          etag: diskEntry.etag,
          timestamp: Date.now()
        })
        return diskEntry.content
      }
    } catch (e) {
      // 缓存损坏,忽略
    }

    return null
  }

  async set(key: string, content: string, options: CacheOptions = {}): Promise<void> {
    const etag = this.generateETag(content)
    const entry: CacheEntry = {
      content,
      etag,
      timestamp: Date.now(),
      ttl: options.ttl || 0
    }

    // 写入内存缓存
    this.memoryCache.set(key, entry)

    // 持久化到磁盘
    if (options.persistent) {
      await this.diskCache.set(key, {
        content,
        etag
      })
    }
  }

  private generateETag(content: string): string {
    const hash = crypto.createHash('sha1')
    hash.update(content)
    return hash.digest('hex')
  }

  private isExpired(entry: CacheEntry): boolean {
    if (entry.ttl === 0) return false
    return Date.now() - entry.timestamp > entry.ttl
  }
}

HTTP 缓存优化

class HTTPCacheManager {
  setupCacheHeaders(res: ServerResponse, content: string): void {
    const etag = this.generateETag(content)
    const lastModified = new Date().toUTCString()

    // 设置缓存头
    res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
    res.setHeader('ETag', etag)
    res.setHeader('Last-Modified', lastModified)

    // 检查客户端缓存
    if (this.request) {
      const ifNoneMatch = this.request.headers['if-none-match']
      const ifModifiedSince = this.request.headers['if-modified-since']

      if ((ifNoneMatch && ifNoneMatch === etag) ||
          (ifModifiedSince && ifModifiedSince === lastModified)) {
        res.statusCode = 304
        res.end()
        return true
      }
    }

    return false
  }
}

进阶架构特性

虚拟模块系统

// 虚拟模块插件示例
export function virtualModulePlugin(): Plugin {
  return {
    name: 'virtual-module',
    resolveId(id) {
      if (id === 'virtual:env') {
        return '\0virtual:env'
      }
    },
    load(id) {
      if (id === '\0virtual:env') {
        const env = {
          NODE_ENV: process.env.NODE_ENV,
          VERSION: '1.0.0'
        }
        return `export default ${JSON.stringify(env)}`
      }
    }
  }
}

// 使用虚拟模块
import env from 'virtual:env'
console.log(env) // { NODE_ENV: 'development', VERSION: '1.0.0' }

Source Map 生成

class SourceMapGenerator {
  async generateSourceMap(
    originalCode: string,
    transformedCode: string,
    filePath: string
  ): Promise<SourceMap> {
    const sourceMap: SourceMap = {
      version: 3,
      file: path.basename(filePath),
      sourceRoot: '',
      sources: [filePath],
      names: [],
      mappings: ''

    }

    // 使用 magic-string 生成精确的 mappings
    const magicString = new MagicString(originalCode)
    const transformed = new MagicString(transformedCode)

    // 映射转换的位置
    const mappings = this.generateMappings(originalCode, transformedCode)
    sourceMap.mappings = mappings

    return sourceMap
  }

  private generateMappings(original: string, transformed: string): string {
    // 使用 VLQ 编码生成 mappings
    const originalLines = original.split('\n')
    const transformedLines = transformed.split('\n')

    const mappings: string[] = []
    let currentLine = 0
    let currentColumn = 0

    for (let i = 0; i < transformedLines.length; i++) {
      const lineMappings: number[][] = []

      for (let j = 0; j < transformedLines[i].length; j++) {
        // 找到原始代码中的对应位置
        const { line, column } = this.findOriginalPosition(
          transformedLines[i],
          j,
          originalLines
        )

        if (line !== -1) {
          lineMappings.push([
            j - currentColumn,    // 生成的列偏移
            line - currentLine,   // 原始行偏移
            column                // 原始列
          ])

          currentColumn = j
          currentLine = line
        }
      }

      // 使用 VLQ 编码
      mappings.push(lineMappings.map(mapping =>
        this.encodeVLQ(mapping)
      ).join(','))

      currentLine = transformedLines.length
      currentColumn = 0
    }

    return mappings.join(';')
  }
}

架构对比

与传统构建工具对比

特性 Vite Webpack Parcel
启动原理 ESM 按需编译 全量打包 零配置打包
HMR 机制 模块级更新 全局重编译 智能更新
构建产出 Rollup 内置 内置
配置复杂度 低 高 极低
插件生态 新兴 成熟 有限
开发体验 优秀 可优化 良好
生产构建 高质量 高质量 良好

适用场景分析

graph TD
    A[项目类型] --> B{选择构建工具}
    B -->|React/Vue SPA| C[Vite 推荐]
    B -->|复杂构建需求| D[Webpack]
    B -->|零配置快速开发| E[Parcel]
    B -->|Next.js SSR| F[Next.js 内置]
    
    C --> G[👍 启动快<br/>👍 HMR 优秀<br/>👍 配置简单]
    D --> H[👍 生态成熟<br/>👍 高度可定制<br/>👎 复杂]
    E --> I[👍 开箱即用<br/>👍 智能配置<br/>👎 生态有限]

理解 Vite 的架构原理,不仅能帮助我们更好地使用它,还能为开发自定义插件和解决复杂问题提供理论支撑。Vite 的成功在于它巧妙地利用了现代浏览器和工具链的能力,选择了正确的技术路径。

相关文档: