Update
This commit is contained in:
@@ -0,0 +1,903 @@
|
||||
---
|
||||
tags: [DEV, VITE, architecture, internals]
|
||||
create time: 2026-04-18
|
||||
---
|
||||
|
||||
# Vite 架构原理
|
||||
|
||||
## 核心设计理念
|
||||
|
||||
Vite 的核心创新在于**利用浏览器原生能力和分层优化策略**,将开发环境和生产环境采用完全不同的处理方式。
|
||||
|
||||
```mermaid
|
||||
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 服务器]
|
||||
```
|
||||
|
||||
## 开发环境架构
|
||||
|
||||
### 服务启动流程
|
||||
|
||||
```mermaid
|
||||
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 请求处理流程
|
||||
|
||||
```mermaid
|
||||
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[返回资源引用]
|
||||
```
|
||||
|
||||
### 请求处理实现
|
||||
|
||||
```typescript
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 模块系统实现
|
||||
|
||||
### 模块图结构
|
||||
|
||||
```mermaid
|
||||
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]
|
||||
```
|
||||
|
||||
### 模块节点实现
|
||||
|
||||
```typescript
|
||||
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 编译器
|
||||
|
||||
### 源码转换流水线
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[原始源代码] --> B[Plugin Transform 钩子]
|
||||
B --> C[语法转换]
|
||||
C --> D[依赖注入]
|
||||
D --> E[包装 ESM]
|
||||
E --> F[生成 Source Map]
|
||||
F --> G[返回处理后的代码]
|
||||
```
|
||||
|
||||
### 依赖预构建
|
||||
|
||||
```typescript
|
||||
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 工作机制
|
||||
|
||||
```mermaid
|
||||
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 协议
|
||||
|
||||
```typescript
|
||||
// 服务端发送的 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
|
||||
|
||||
```typescript
|
||||
// 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__()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 插件系统架构
|
||||
|
||||
### 插件生命周期
|
||||
|
||||
```mermaid
|
||||
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]
|
||||
```
|
||||
|
||||
### 插件容器实现
|
||||
|
||||
```typescript
|
||||
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 集成
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[入口文件] --> B[依赖图构建]
|
||||
B --> C[模块解析]
|
||||
C --> D[代码转换]
|
||||
D --> E[代码分割]
|
||||
E --> F[Tree Shaking]
|
||||
F --> G[minification]
|
||||
G --> H[输出生成]
|
||||
```
|
||||
|
||||
### 构建流程实现
|
||||
|
||||
```typescript
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能优化机制
|
||||
|
||||
### 缓存策略
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[请求模块] --> B{缓存检查}
|
||||
B -->|内存缓存命中| C[返回缓存]
|
||||
B -->|磁盘缓存命中| D[加载并返回]
|
||||
B -->|缓存未命中| E[编译模块]
|
||||
E --> F[写入缓存]
|
||||
F --> C
|
||||
```
|
||||
|
||||
### 多层缓存实现
|
||||
|
||||
```typescript
|
||||
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 缓存优化
|
||||
|
||||
```typescript
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 进阶架构特性
|
||||
|
||||
### 虚拟模块系统
|
||||
|
||||
```typescript
|
||||
// 虚拟模块插件示例
|
||||
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 生成
|
||||
|
||||
```typescript
|
||||
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 | 内置 | 内置 |
|
||||
| 配置复杂度 | 低 | 高 | 极低 |
|
||||
| 插件生态 | 新兴 | 成熟 | 有限 |
|
||||
| 开发体验 | 优秀 | 可优化 | 良好 |
|
||||
| 生产构建 | 高质量 | 高质量 | 良好 |
|
||||
|
||||
### 适用场景分析
|
||||
|
||||
```mermaid
|
||||
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 的成功在于它巧妙地利用了现代浏览器和工具链的能力,选择了正确的技术路径。
|
||||
|
||||
相关文档:
|
||||
- [[DEV/VITE/配置详解.md]] - 配置与插件开发
|
||||
- [[DEV/VITE/优化实践.md]] - 架构层面的性能优化
|
||||
Reference in New Issue
Block a user