Turbopack 核心特性 #

增量编译 #

什么是增量编译? #

增量编译是指只编译发生变化的部分,而不是每次都重新编译整个项目。Turbopack 通过增量编译实现了极致的构建速度。

text
┌─────────────────────────────────────────────────────────────┐
│                    增量编译原理                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   传统编译:                                                  │
│   ┌─────────┐   ┌─────────┐   ┌─────────┐                  │
│   │ 全部文件 │ → │ 全部编译 │ → │ 全部输出 │                  │
│   └─────────┘   └─────────┘   └─────────┘                  │
│   时间: O(n)                                                 │
│                                                              │
│   增量编译:                                                  │
│   ┌─────────┐   ┌─────────┐   ┌─────────┐                  │
│   │ 变化文件 │ → │ 增量编译 │ → │ 增量输出 │                  │
│   └─────────┘   └─────────┘   └─────────┘                  │
│   时间: O(1)                                                 │
│                                                              │
└─────────────────────────────────────────────────────────────┘

变化检测 #

Turbopack 使用多种方式检测文件变化:

rust
pub struct FileWatcher {
    watcher: RecommendedWatcher,
    cache: Arc<Cache>,
}

impl FileWatcher {
    pub fn detect_changes(&self) -> Vec<Change> {
        let mut changes = Vec::new();
        
        for event in self.watcher.events() {
            match event.kind {
                EventKind::Create(_) => changes.push(Change::Added),
                EventKind::Modify(_) => changes.push(Change::Modified),
                EventKind::Remove(_) => changes.push(Change::Deleted),
                _ => {}
            }
        }
        
        changes
    }
}

增量编译流程 #

text
┌─────────────────────────────────────────────────────────────┐
│                    增量编译流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   1. 文件变化检测                                            │
│      └── 监听文件系统事件                                    │
│                                                              │
│   2. 依赖图更新                                              │
│      └── 更新受影响的模块依赖                                │
│                                                              │
│   3. 缓存失效                                                │
│      └── 标记需要重新编译的模块                              │
│                                                              │
│   4. 增量编译                                                │
│      └── 只编译变化的模块                                    │
│                                                              │
│   5. 缓存更新                                                │
│      └── 保存新的编译结果                                    │
│                                                              │
│   6. 输出更新                                                │
│      └── 生成新的输出文件                                    │
│                                                              │
└─────────────────────────────────────────────────────────────┘

实际效果 #

javascript
// 修改前: src/utils/format.ts
export function formatDate(date: Date): string {
  return date.toLocaleDateString();
}

// 修改后: src/utils/format.ts
export function formatDate(date: Date): string {
  return date.toLocaleDateString('zh-CN');
}

// Turbopack 只重新编译:
// 1. src/utils/format.ts
// 2. 导入 format.ts 的文件(增量更新)
// 其他文件使用缓存

持久化缓存 #

缓存架构 #

text
┌─────────────────────────────────────────────────────────────┐
│                    缓存层级                                  │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   ┌─────────────────────────────────────────────────────┐  │
│   │              L1: 内存缓存                             │  │
│   │   - 最快访问速度                                      │  │
│   │   - 当前会话有效                                      │  │
│   │   - 自动管理内存                                      │  │
│   └─────────────────────────────────────────────────────┘  │
│                           │                                  │
│                           ▼                                  │
│   ┌─────────────────────────────────────────────────────┐  │
│   │              L2: 磁盘缓存                             │  │
│   │   - 持久化存储                                        │  │
│   │   - 跨会话有效                                        │  │
│   │   - 增量更新                                          │  │
│   └─────────────────────────────────────────────────────┘  │
│                           │                                  │
│                           ▼                                  │
│   ┌─────────────────────────────────────────────────────┐  │
│   │              L3: 远程缓存                             │  │
│   │   - 团队共享                                          │  │
│   │   - CI/CD 集成                                        │  │
│   │   - 按需下载                                          │  │
│   └─────────────────────────────────────────────────────┘  │
│                                                              │
└─────────────────────────────────────────────────────────────┘

缓存结构 #

text
.turbo/
├── cache/
│   ├── analysis/
│   │   ├── a1b2c3d4.json
│   │   └── e5f6g7h8.json
│   ├── emit/
│   │   ├── output-a1b2c3d4.js
│   │   └── output-e5f6g7h8.js
│   └── source-maps/
│       ├── output-a1b2c3d4.js.map
│       └── output-e5f6g7h8.js.map
└── info.json

缓存键计算 #

rust
pub struct CacheKey {
    content_hash: String,
    dependencies: Vec<String>,
    config_hash: String,
    env_hash: String,
}

impl CacheKey {
    pub fn compute(source: &str, config: &Config) -> Self {
        let content_hash = sha256(source);
        let dependencies = resolve_dependencies(source);
        let config_hash = sha256(&config.to_string());
        let env_hash = sha256(&env::vars().collect::<String>());
        
        Self {
            content_hash,
            dependencies,
            config_hash,
            env_hash,
        }
    }
}

缓存命中 #

rust
pub fn get_or_compile(key: &CacheKey) -> Result<CompiledModule> {
    if let Some(cached) = memory_cache.get(key) {
        return Ok(cached);
    }
    
    if let Some(cached) = disk_cache.get(key) {
        memory_cache.insert(key.clone(), cached.clone());
        return Ok(cached);
    }
    
    let compiled = compile(key)?;
    memory_cache.insert(key.clone(), compiled.clone());
    disk_cache.insert(key.clone(), compiled.clone());
    
    Ok(compiled)
}

缓存失效策略 #

rust
pub enum InvalidationReason {
    SourceChanged,
    DependencyChanged,
    ConfigChanged,
    EnvChanged,
    CacheExpired,
}

pub fn check_invalidation(key: &CacheKey) -> Option<InvalidationReason> {
    if source_changed(key) {
        return Some(InvalidationReason::SourceChanged);
    }
    
    if dependency_changed(key) {
        return Some(InvalidationReason::DependencyChanged);
    }
    
    if config_changed(key) {
        return Some(InvalidationReason::ConfigChanged);
    }
    
    None
}

函数级缓存 #

为什么是函数级? #

传统打包器使用文件级缓存,但 Turbopack 实现了更细粒度的函数级缓存:

text
┌─────────────────────────────────────────────────────────────┐
│                    缓存粒度对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   文件级缓存:                                                │
│   ┌─────────────────────────────────────┐                  │
│   │  function a() { }                   │                  │
│   │  function b() { }  ← 修改这里       │                  │
│   │  function c() { }                   │                  │
│   └─────────────────────────────────────┘                  │
│   结果: 整个文件需要重新编译                                │
│                                                              │
│   函数级缓存:                                                │
│   ┌─────────────┐ ┌─────────────┐ ┌─────────────┐         │
│   │ function a()│ │ function b()│ │ function c()│         │
│   │   [cached]  │ │ [recompile] │ │   [cached]  │         │
│   └─────────────┘ └─────────────┘ └─────────────┘         │
│   结果: 只重新编译函数 b                                    │
│                                                              │
└─────────────────────────────────────────────────────────────┘

函数级缓存实现 #

rust
pub struct FunctionCache {
    functions: HashMap<FunctionId, CachedFunction>,
}

pub struct CachedFunction {
    id: FunctionId,
    source: String,
    hash: String,
    compiled: Vec<u8>,
    dependencies: Vec<FunctionId>,
}

impl FunctionCache {
    pub fn get_or_compile(&mut self, func: &Function) -> Result<&CachedFunction> {
        let hash = self.compute_hash(func);
        
        if let Some(cached) = self.functions.get(&func.id) {
            if cached.hash == hash {
                return Ok(cached);
            }
        }
        
        let compiled = self.compile_function(func)?;
        self.functions.insert(func.id.clone(), compiled);
        
        Ok(self.functions.get(&func.id).unwrap())
    }
}

实际效果 #

typescript
// utils.ts
export function formatPrice(price: number): string {
  return `$${price.toFixed(2)}`;
}

export function formatDate(date: Date): string {
  return date.toLocaleDateString();
}

export function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

如果只修改 formatDate

text
编译结果:
├── formatPrice: [使用缓存]
├── formatDate:  [重新编译]
└── capitalize:  [使用缓存]

懒加载编译 #

懒加载原理 #

Turbopack 只编译当前请求需要的代码:

text
┌─────────────────────────────────────────────────────────────┐
│                    懒加载编译流程                            │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   浏览器请求: /page-a                                        │
│       │                                                      │
│       ▼                                                      │
│   ┌─────────────────────────────────────────────────────┐  │
│   │  只编译 page-a 相关模块                              │  │
│   │  ├── page-a.tsx                                     │  │
│   │  ├── components/Header.tsx                          │  │
│   │  └── utils/format.ts                                │  │
│   └─────────────────────────────────────────────────────┘  │
│                                                              │
│   其他页面: /page-b, /page-c                                │
│       │                                                      │
│       └── 不编译,等待请求时再编译                          │
│                                                              │
└─────────────────────────────────────────────────────────────┘

请求驱动编译 #

rust
pub struct LazyCompiler {
    module_graph: ModuleGraph,
    compiled: HashSet<ModuleId>,
}

impl LazyCompiler {
    pub async fn compile_for_request(
        &mut self,
        request_path: &str,
    ) -> Result<Vec<CompiledModule>> {
        let entry = self.module_graph.resolve_entry(request_path)?;
        let mut result = Vec::new();
        
        for module_id in self.module_graph.dependencies_of(entry) {
            if !self.compiled.contains(&module_id) {
                let compiled = self.compile_module(&module_id).await?;
                result.push(compiled);
                self.compiled.insert(module_id);
            }
        }
        
        Ok(result)
    }
}

按需编译示例 #

text
项目结构:
src/
├── pages/
│   ├── home.tsx      # 访问 / 时编译
│   ├── about.tsx     # 访问 /about 时编译
│   ├── contact.tsx   # 访问 /contact 时编译
│   └── blog/
│       ├── index.tsx # 访问 /blog 时编译
│       └── [slug].tsx
└── components/
    ├── Header.tsx    # 被 home.tsx 引用时编译
    └── Footer.tsx

启动时:
- 只编译入口文件
- 其他文件等待请求

访问 /about 时:
- 编译 about.tsx
- 编译 about.tsx 的依赖
- 其他页面不编译

性能对比 #

text
┌─────────────────────────────────────────────────────────────┐
│                    冷启动时间对比                            │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   Webpack (全量编译):                                        │
│   ████████████████████████████████████████  30s             │
│                                                              │
│   Vite (按需编译):                                           │
│   ████████  2.5s                                             │
│                                                              │
│   Turbopack (懒加载编译):                                    │
│   █  0.5s                                                    │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Rust 高性能实现 #

为什么选择 Rust? #

text
┌─────────────────────────────────────────────────────────────┐
│                    Rust 优势                                 │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   1. 性能                                                    │
│      ├── 零成本抽象                                          │
│      ├── 无 GC 停顿                                          │
│      └── 接近 C/C++ 性能                                     │
│                                                              │
│   2. 并发                                                    │
│      ├── 无数据竞争                                          │
│      ├── 安全的多线程                                        │
│      └── 高效的异步运行时                                    │
│                                                              │
│   3. 内存安全                                                │
│      ├── 编译时检查                                          │
│      ├── 无空指针                                            │
│      └── 无缓冲区溢出                                        │
│                                                              │
│   4. 工具链                                                  │
│      ├── Cargo 包管理                                        │
│      ├── 内置测试                                            │
│      └── 优秀的文档                                          │
│                                                              │
└─────────────────────────────────────────────────────────────┘

核心数据结构 #

rust
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

pub struct ModuleGraph {
    modules: Arc<RwLock<HashMap<ModuleId, Module>>>,
    edges: Vec<Edge>,
    cache: PersistentCache,
}

pub struct Module {
    id: ModuleId,
    source: Source,
    ast: Option<AST>,
    dependencies: Vec<ModuleId>,
    compiled: Option<CompiledOutput>,
}

pub struct Edge {
    from: ModuleId,
    to: ModuleId,
    kind: DependencyKind,
}

并行编译 #

rust
use rayon::prelude::*;

impl ModuleGraph {
    pub fn compile_parallel(&mut self) -> Result<Vec<CompiledModule>> {
        let modules: Vec<&mut Module> = self.modules
            .values_mut()
            .collect();
        
        modules
            .par_iter_mut()
            .map(|module| self.compile_module(module))
            .collect()
    }
}

异步 I/O #

rust
use tokio::fs;
use tokio::io::AsyncReadExt;

pub async fn read_source(path: &Path) -> Result<String> {
    let mut file = fs::File::open(path).await?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).await?;
    Ok(contents)
}

pub async fn compile_module(path: &Path) -> Result<CompiledModule> {
    let source = read_source(path).await?;
    let ast = parse(&source)?;
    let output = transform(&ast)?;
    Ok(CompiledModule { source, ast, output })
}

内存管理 #

rust
pub struct MemoryPool {
    chunks: Vec<Box<[u8]>>,
    current: usize,
}

impl MemoryPool {
    pub fn allocate(&mut self, size: usize) -> &mut [u8] {
        if self.current + size > self.chunks.len() {
            self.chunks.push(vec![0; size.max(1024 * 1024)].into_boxed_slice());
        }
        
        let start = self.current;
        self.current += size;
        &mut self.chunks.last_mut().unwrap()[start..self.current]
    }
}

HMR 热更新 #

HMR 架构 #

text
┌─────────────────────────────────────────────────────────────┐
│                    HMR 流程                                  │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   文件变化                                                    │
│       │                                                      │
│       ▼                                                      │
│   ┌─────────────┐                                            │
│   │ 文件监听器  │                                            │
│   └──────┬──────┘                                            │
│          │                                                   │
│          ▼                                                   │
│   ┌─────────────┐                                            │
│   │ 增量编译    │                                            │
│   └──────┬──────┘                                            │
│          │                                                   │
│          ▼                                                   │
│   ┌─────────────┐                                            │
│   │ HMR 服务器  │                                            │
│   └──────┬──────┘                                            │
│          │                                                   │
│          ▼                                                   │
│   ┌─────────────┐                                            │
│   │ WebSocket   │                                            │
│   └──────┬──────┘                                            │
│          │                                                   │
│          ▼                                                   │
│   ┌─────────────┐                                            │
│   │ 浏览器更新  │                                            │
│   └─────────────┘                                            │
│                                                              │
└─────────────────────────────────────────────────────────────┘

HMR 实现 #

rust
pub struct HmrServer {
    clients: Vec<WebSocket>,
    watcher: FileWatcher,
    compiler: Compiler,
}

impl HmrServer {
    pub async fn handle_change(&mut self, change: FileChange) -> Result<()> {
        let compiled = self.compiler.compile_incremental(&change).await?;
        
        let update = HmrUpdate {
            modules: compiled.modules,
            hash: compiled.hash,
        };
        
        for client in &self.clients {
            client.send(serde_json::to_string(&update)?).await?;
        }
        
        Ok(())
    }
}

客户端 HMR #

typescript
// hmr-client.ts
interface HmrUpdate {
  modules: string[];
  hash: string;
}

const socket = new WebSocket('ws://localhost:3000/_turbo/hmr');

socket.onmessage = (event) => {
  const update: HmrUpdate = JSON.parse(event.data);
  
  update.modules.forEach((moduleId) => {
    const module = __webpack_modules__[moduleId];
    if (module && module.hot) {
      module.hot.accept();
    }
  });
  
  console.log('[HMR] Updated modules:', update.modules);
};

HMR 性能 #

text
┌─────────────────────────────────────────────────────────────┐
│                    HMR 时间对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   项目规模: 3000 模块                                        │
│                                                              │
│   Webpack:                                                   │
│   ████████████████████████████████  3000ms                  │
│                                                              │
│   Vite:                                                      │
│   ████████  200ms                                            │
│                                                              │
│   Turbopack:                                                 │
│   █  10ms                                                    │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Tree Shaking #

Tree Shaking 原理 #

text
┌─────────────────────────────────────────────────────────────┐
│                    Tree Shaking                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   源代码:                                                    │
│   ┌─────────────────────────────────────┐                  │
│   │  export function used() { }         │                  │
│   │  export function unused() { }       │                  │
│   │  export function alsoUnused() { }   │                  │
│   └─────────────────────────────────────┘                  │
│                                                              │
│   导入:                                                      │
│   import { used } from './utils';                           │
│                                                              │
│   打包结果:                                                  │
│   ┌─────────────────────────────────────┐                  │
│   │  function used() { }                │                  │
│   └─────────────────────────────────────┘                  │
│                                                              │
└─────────────────────────────────────────────────────────────┘

静态分析 #

rust
pub struct TreeShaker {
    used_exports: HashSet<ExportId>,
}

impl TreeShaker {
    pub fn analyze(&mut self, entry: &ModuleId, graph: &ModuleGraph) {
        let mut stack = vec![entry];
        
        while let Some(module_id) = stack.pop() {
            let module = graph.get(module_id);
            
            for import in &module.imports {
                self.used_exports.insert(import.export_id.clone());
                stack.push(&import.module_id);
            }
        }
    }
    
    pub fn shake(&self, module: &Module) -> ShakedModule {
        let kept_functions: Vec<_> = module.functions
            .iter()
            .filter(|f| self.used_exports.contains(&f.export_id))
            .collect();
        
        ShakedModule {
            functions: kept_functions,
        }
    }
}

副作用标记 #

json
// package.json
{
  "name": "my-library",
  "sideEffects": false
}
json
// 或指定有副作用的文件
{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.js"
  ]
}

Source Map #

Source Map 生成 #

rust
pub struct SourceMapGenerator {
    sources: Vec<String>,
    names: Vec<String>,
    mappings: Vec<Mapping>,
}

impl SourceMapGenerator {
    pub fn add_mapping(
        &mut self,
        generated: Position,
        original: Position,
        source: &str,
        name: Option<&str>,
    ) {
        self.sources.push(source.to_string());
        if let Some(n) = name {
            self.names.push(n.to_string());
        }
        self.mappings.push(Mapping {
            generated,
            original,
            source_index: self.sources.len() - 1,
            name_index: name.map(|_| self.names.len() - 1),
        });
    }
    
    pub fn to_json(&self) -> String {
        serde_json::to_string(&SourceMapJson {
            version: 3,
            sources: &self.sources,
            names: &self.names,
            mappings: &self.encode_mappings(),
        }).unwrap()
    }
}

Source Map 配置 #

javascript
// next.config.js
module.exports = {
  experimental: {
    turbo: {
      sourceMaps: true,
      sourceMapOptions: {
        exclude: /node_modules/,
        include: /src/,
      },
    },
  },
}

下一步 #

现在你已经深入了解了 Turbopack 的核心特性,接下来学习 迁移指南 将现有项目迁移到 Turbopack!

最后更新:2026-03-28