10.1 TypeScript 编译性能优化

10.1.1 编译器配置优化

// tsconfig.json - 性能优化配置
{
  "compilerOptions": {
    // 基础配置
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    
    // 性能优化配置
    "incremental": true,                    // 启用增量编译
    "tsBuildInfoFile": ".tsbuildinfo",     // 构建信息文件
    "skipLibCheck": true,                  // 跳过库文件类型检查
    "skipDefaultLibCheck": true,           // 跳过默认库检查
    
    // 严格性配置(影响性能)
    "strict": true,
    "noUnusedLocals": false,               // 在开发时可以关闭
    "noUnusedParameters": false,           // 在开发时可以关闭
    
    // 模块解析优化
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@/components/*": ["src/components/*"],
      "@/utils/*": ["src/utils/*"]
    },
    
    // 输出配置
    "outDir": "dist",
    "rootDir": "src",
    "removeComments": true,                // 移除注释减少输出大小
    "declaration": false,                  // 开发时可以关闭声明文件生成
    "sourceMap": false                     // 生产环境可以关闭源码映射
  },
  
  // 包含和排除配置
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist",
    "**/*.test.ts",
    "**/*.spec.ts"
  ],
  
  // 项目引用(大型项目)
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/utils" }
  ]
}

10.1.2 项目引用和增量编译

// packages/core/tsconfig.json - 核心包配置
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "composite": true,                     // 启用项目引用
    "declaration": true,                   // 生成声明文件
    "declarationMap": true,               // 生成声明映射
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src/**/*"],
  "exclude": ["**/*.test.ts"]
}

// packages/utils/tsconfig.json - 工具包配置
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src/**/*"],
  "references": [
    { "path": "../core" }
  ]
}

// 根目录 tsconfig.json - 主项目配置
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "noEmit": true                        // 主项目不输出,只做类型检查
  },
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/utils" }
  ],
  "include": []
}

10.1.3 编译性能监控

// scripts/build-performance.ts - 编译性能监控
import { performance } from 'perf_hooks';
import * as ts from 'typescript';
import * as fs from 'fs';
import * as path from 'path';

interface CompilationMetrics {
  totalTime: number;
  parseTime: number;
  bindTime: number;
  checkTime: number;
  emitTime: number;
  filesCount: number;
  linesOfCode: number;
  memoryUsage: {
    heapUsed: number;
    heapTotal: number;
    external: number;
  };
}

class CompilationProfiler {
  private startTime: number = 0;
  private metrics: Partial<CompilationMetrics> = {};
  
  startProfiling(): void {
    this.startTime = performance.now();
    this.metrics = {};
  }
  
  recordPhase(phase: keyof CompilationMetrics, duration: number): void {
    this.metrics[phase] = duration as any;
  }
  
  endProfiling(): CompilationMetrics {
    const totalTime = performance.now() - this.startTime;
    const memoryUsage = process.memoryUsage();
    
    return {
      totalTime,
      parseTime: this.metrics.parseTime || 0,
      bindTime: this.metrics.bindTime || 0,
      checkTime: this.metrics.checkTime || 0,
      emitTime: this.metrics.emitTime || 0,
      filesCount: this.metrics.filesCount || 0,
      linesOfCode: this.metrics.linesOfCode || 0,
      memoryUsage: {
        heapUsed: memoryUsage.heapUsed,
        heapTotal: memoryUsage.heapTotal,
        external: memoryUsage.external
      }
    };
  }
}

class TypeScriptCompiler {
  private profiler = new CompilationProfiler();
  
  async compile(configPath: string): Promise<CompilationMetrics> {
    this.profiler.startProfiling();
    
    // 读取配置
    const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
    if (configFile.error) {
      throw new Error(`Failed to read config: ${configFile.error.messageText}`);
    }
    
    const parsedConfig = ts.parseJsonConfigFileContent(
      configFile.config,
      ts.sys,
      path.dirname(configPath)
    );
    
    // 创建程序
    const parseStart = performance.now();
    const program = ts.createProgram({
      rootNames: parsedConfig.fileNames,
      options: parsedConfig.options,
      configFileParsingDiagnostics: parsedConfig.errors
    });
    const parseTime = performance.now() - parseStart;
    this.profiler.recordPhase('parseTime', parseTime);
    
    // 类型检查
    const checkStart = performance.now();
    const diagnostics = ts.getPreEmitDiagnostics(program);
    const checkTime = performance.now() - checkStart;
    this.profiler.recordPhase('checkTime', checkTime);
    
    // 输出文件
    const emitStart = performance.now();
    const emitResult = program.emit();
    const emitTime = performance.now() - emitStart;
    this.profiler.recordPhase('emitTime', emitTime);
    
    // 收集统计信息
    const sourceFiles = program.getSourceFiles();
    const filesCount = sourceFiles.length;
    const linesOfCode = sourceFiles.reduce((total, file) => {
      return total + file.getLineAndCharacterOfPosition(file.getEnd()).line + 1;
    }, 0);
    
    this.profiler.recordPhase('filesCount', filesCount);
    this.profiler.recordPhase('linesOfCode', linesOfCode);
    
    // 处理诊断信息
    const allDiagnostics = diagnostics.concat(emitResult.diagnostics);
    if (allDiagnostics.length > 0) {
      console.warn('Compilation warnings/errors:');
      allDiagnostics.forEach(diagnostic => {
        const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
        console.warn(message);
      });
    }
    
    return this.profiler.endProfiling();
  }
  
  generatePerformanceReport(metrics: CompilationMetrics): string {
    const report = `
# TypeScript 编译性能报告

## 总体指标
- 总编译时间: ${metrics.totalTime.toFixed(2)}ms
- 文件数量: ${metrics.filesCount}
- 代码行数: ${metrics.linesOfCode}
- 平均每文件编译时间: ${(metrics.totalTime / metrics.filesCount).toFixed(2)}ms
- 平均每行编译时间: ${(metrics.totalTime / metrics.linesOfCode).toFixed(4)}ms

## 阶段耗时
- 解析阶段: ${metrics.parseTime.toFixed(2)}ms (${((metrics.parseTime / metrics.totalTime) * 100).toFixed(1)}%)
- 绑定阶段: ${metrics.bindTime.toFixed(2)}ms (${((metrics.bindTime / metrics.totalTime) * 100).toFixed(1)}%)
- 类型检查: ${metrics.checkTime.toFixed(2)}ms (${((metrics.checkTime / metrics.totalTime) * 100).toFixed(1)}%)
- 代码生成: ${metrics.emitTime.toFixed(2)}ms (${((metrics.emitTime / metrics.totalTime) * 100).toFixed(1)}%)

## 内存使用
- 堆内存使用: ${(metrics.memoryUsage.heapUsed / 1024 / 1024).toFixed(2)}MB
- 堆内存总量: ${(metrics.memoryUsage.heapTotal / 1024 / 1024).toFixed(2)}MB
- 外部内存: ${(metrics.memoryUsage.external / 1024 / 1024).toFixed(2)}MB

## 性能建议
${this.generateOptimizationSuggestions(metrics)}
    `;
    
    return report.trim();
  }
  
  private generateOptimizationSuggestions(metrics: CompilationMetrics): string {
    const suggestions: string[] = [];
    
    // 编译时间建议
    if (metrics.totalTime > 10000) {
      suggestions.push('- 考虑启用增量编译 ("incremental": true)');
      suggestions.push('- 使用项目引用分割大型项目');
    }
    
    // 类型检查建议
    const checkRatio = metrics.checkTime / metrics.totalTime;
    if (checkRatio > 0.6) {
      suggestions.push('- 类型检查耗时较长,考虑启用 "skipLibCheck"');
      suggestions.push('- 减少复杂的类型计算和条件类型');
    }
    
    // 内存使用建议
    const heapUsageMB = metrics.memoryUsage.heapUsed / 1024 / 1024;
    if (heapUsageMB > 500) {
      suggestions.push('- 内存使用较高,考虑分割项目或增加 Node.js 内存限制');
    }
    
    // 文件数量建议
    if (metrics.filesCount > 1000) {
      suggestions.push('- 文件数量较多,考虑使用 "exclude" 排除不必要的文件');
    }
    
    return suggestions.length > 0 ? suggestions.join('\n') : '- 编译性能良好,无需优化';
  }
}

// 使用示例
async function main() {
  const compiler = new TypeScriptCompiler();
  
  try {
    const metrics = await compiler.compile('./tsconfig.json');
    const report = compiler.generatePerformanceReport(metrics);
    
    console.log(report);
    
    // 保存报告
    fs.writeFileSync('compilation-performance.md', report);
    fs.writeFileSync('compilation-metrics.json', JSON.stringify(metrics, null, 2));
    
  } catch (error) {
    console.error('Compilation failed:', error);
    process.exit(1);
  }
}

if (require.main === module) {
  main();
}

export { TypeScriptCompiler, CompilationMetrics };

10.2 运行时性能优化

10.2.1 类型优化策略

// 避免复杂的类型计算
// ❌ 性能较差的类型
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

type ComplexUnion<T> = T extends string
  ? T extends `${infer A}_${infer B}`
    ? A extends 'user'
      ? B extends 'data'
        ? UserData
        : never
      : never
    : never
  : never;

// ✅ 性能更好的类型
interface ReadonlyUser {
  readonly id: string;
  readonly name: string;
  readonly email: string;
}

type UserDataType = 'user_data';
interface UserData {
  type: UserDataType;
  user: ReadonlyUser;
}

// 使用类型断言而非复杂类型推断
function processUserData(data: unknown): UserData {
  // ❌ 复杂的类型守卫
  // if (isComplexUserData(data)) {
  //   return data;
  // }
  
  // ✅ 简单的类型断言
  return data as UserData;
}

// 优化泛型约束
// ❌ 过度复杂的泛型
interface ComplexProcessor<
  T extends Record<string, any>,
  K extends keyof T,
  V extends T[K]
> {
  process(key: K, value: V): T[K];
}

// ✅ 简化的泛型
interface SimpleProcessor<T> {
  process<K extends keyof T>(key: K, value: T[K]): T[K];
}

// 使用联合类型而非条件类型
// ❌ 条件类型
type ApiResponse<T> = T extends string
  ? { message: T }
  : T extends number
  ? { code: T }
  : { data: T };

// ✅ 联合类型
type ApiResponse2 = 
  | { message: string }
  | { code: number }
  | { data: any };

// 缓存复杂类型计算
type CachedComplexType<T> = T extends string ? string[] : T[];

// 使用类型别名缓存
type StringArray = string[];
type CachedType<T> = T extends string ? StringArray : T[];

10.2.2 代码分割和懒加载

// src/utils/lazy-loader.ts - 懒加载工具
export class LazyLoader {
  private static cache = new Map<string, Promise<any>>();
  
  static async loadModule<T = any>(moduleFactory: () => Promise<T>): Promise<T> {
    const moduleKey = moduleFactory.toString();
    
    if (this.cache.has(moduleKey)) {
      return this.cache.get(moduleKey)!;
    }
    
    const modulePromise = moduleFactory();
    this.cache.set(moduleKey, modulePromise);
    
    return modulePromise;
  }
  
  static async loadComponent<T extends React.ComponentType<any>>(
    componentFactory: () => Promise<{ default: T }>
  ): Promise<T> {
    const module = await this.loadModule(componentFactory);
    return module.default;
  }
  
  static preload(moduleFactory: () => Promise<any>): void {
    // 预加载但不等待
    this.loadModule(moduleFactory).catch(console.error);
  }
  
  static clearCache(): void {
    this.cache.clear();
  }
}

// src/components/LazyComponent.tsx - 懒加载组件
import React, { Suspense, lazy } from 'react';
import { LazyLoader } from '../utils/lazy-loader';

// 懒加载组件定义
const LazyUserList = lazy(() => 
  LazyLoader.loadModule(() => import('./UserList'))
);

const LazyUserDetail = lazy(() => 
  LazyLoader.loadModule(() => import('./UserDetail'))
);

const LazyChart = lazy(() => 
  LazyLoader.loadModule(() => import('./Chart'))
);

// 加载状态组件
const LoadingSpinner: React.FC = () => (
  <div className="loading-spinner">
    <div className="spinner"></div>
    <p>Loading...</p>
  </div>
);

// 错误边界组件
class LazyLoadErrorBoundary extends React.Component<
  { children: React.ReactNode },
  { hasError: boolean; error?: Error }
> {
  constructor(props: { children: React.ReactNode }) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('Lazy load error:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <div className="error-boundary">
          <h3>Something went wrong</h3>
          <p>{this.state.error?.message}</p>
          <button onClick={() => this.setState({ hasError: false })}>
            Try Again
          </button>
        </div>
      );
    }
    
    return this.props.children;
  }
}

// 懒加载容器组件
interface LazyWrapperProps {
  component: React.LazyExoticComponent<React.ComponentType<any>>;
  fallback?: React.ComponentType;
  preload?: boolean;
  [key: string]: any;
}

export const LazyWrapper: React.FC<LazyWrapperProps> = ({
  component: Component,
  fallback: Fallback = LoadingSpinner,
  preload = false,
  ...props
}) => {
  // 预加载逻辑
  React.useEffect(() => {
    if (preload) {
      // 预加载组件
      const preloadComponent = Component as any;
      if (preloadComponent._payload && preloadComponent._payload._result === undefined) {
        preloadComponent._payload._result = preloadComponent._payload._fn();
      }
    }
  }, [Component, preload]);
  
  return (
    <LazyLoadErrorBoundary>
      <Suspense fallback={<Fallback />}>
        <Component {...props} />
      </Suspense>
    </LazyLoadErrorBoundary>
  );
};

// 路由级别的代码分割
import { createBrowserRouter, RouteObject } from 'react-router-dom';

const routes: RouteObject[] = [
  {
    path: '/',
    element: <LazyWrapper component={LazyUserList} preload={true} />
  },
  {
    path: '/user/:id',
    element: <LazyWrapper component={LazyUserDetail} />
  },
  {
    path: '/analytics',
    element: <LazyWrapper component={LazyChart} />
  }
];

export const router = createBrowserRouter(routes);

// 预加载策略
export class PreloadStrategy {
  private static preloadedRoutes = new Set<string>();
  
  static preloadRoute(path: string): void {
    if (this.preloadedRoutes.has(path)) {
      return;
    }
    
    this.preloadedRoutes.add(path);
    
    switch (path) {
      case '/user':
        LazyLoader.preload(() => import('./UserDetail'));
        break;
      case '/analytics':
        LazyLoader.preload(() => import('./Chart'));
        break;
    }
  }
  
  static preloadOnHover(element: HTMLElement, path: string): void {
    const handleMouseEnter = () => {
      this.preloadRoute(path);
      element.removeEventListener('mouseenter', handleMouseEnter);
    };
    
    element.addEventListener('mouseenter', handleMouseEnter);
  }
  
  static preloadOnIdle(): void {
    if ('requestIdleCallback' in window) {
      requestIdleCallback(() => {
        // 在浏览器空闲时预加载常用组件
        this.preloadRoute('/user');
        this.preloadRoute('/analytics');
      });
    } else {
      // 降级方案
      setTimeout(() => {
        this.preloadRoute('/user');
        this.preloadRoute('/analytics');
      }, 2000);
    }
  }
}

10.2.3 内存管理优化

// src/utils/memory-manager.ts - 内存管理工具
export class MemoryManager {
  private static observers = new Set<PerformanceObserver>();
  private static memoryThreshold = 50 * 1024 * 1024; // 50MB
  
  static startMemoryMonitoring(): void {
    if (!('memory' in performance)) {
      console.warn('Performance memory API not supported');
      return;
    }
    
    const checkMemory = () => {
      const memory = (performance as any).memory;
      
      if (memory.usedJSHeapSize > this.memoryThreshold) {
        console.warn('High memory usage detected:', {
          used: `${(memory.usedJSHeapSize / 1024 / 1024).toFixed(2)}MB`,
          total: `${(memory.totalJSHeapSize / 1024 / 1024).toFixed(2)}MB`,
          limit: `${(memory.jsHeapSizeLimit / 1024 / 1024).toFixed(2)}MB`
        });
        
        this.triggerGarbageCollection();
      }
    };
    
    // 定期检查内存使用
    setInterval(checkMemory, 30000); // 每30秒检查一次
  }
  
  static triggerGarbageCollection(): void {
    // 清理缓存
    LazyLoader.clearCache();
    
    // 清理事件监听器
    this.cleanupEventListeners();
    
    // 建议浏览器进行垃圾回收
    if ('gc' in window && typeof (window as any).gc === 'function') {
      (window as any).gc();
    }
  }
  
  private static cleanupEventListeners(): void {
    // 清理性能观察器
    this.observers.forEach(observer => {
      observer.disconnect();
    });
    this.observers.clear();
  }
  
  static createWeakCache<K extends object, V>(): WeakMap<K, V> {
    return new WeakMap<K, V>();
  }
  
  static createLRUCache<K, V>(maxSize: number): Map<K, V> & { cleanup(): void } {
    const cache = new Map<K, V>();
    
    const extendedCache = cache as Map<K, V> & { cleanup(): void };
    
    const originalSet = cache.set.bind(cache);
    extendedCache.set = (key: K, value: V) => {
      if (cache.size >= maxSize) {
        const firstKey = cache.keys().next().value;
        cache.delete(firstKey);
      }
      return originalSet(key, value);
    };
    
    extendedCache.cleanup = () => {
      cache.clear();
    };
    
    return extendedCache;
  }
}

// src/hooks/useMemoryOptimization.ts - 内存优化 Hook
import { useEffect, useRef, useCallback } from 'react';

export function useMemoryOptimization() {
  const cleanupFunctions = useRef<(() => void)[]>([]);
  
  const addCleanup = useCallback((cleanup: () => void) => {
    cleanupFunctions.current.push(cleanup);
  }, []);
  
  const createOptimizedCallback = useCallback(<T extends (...args: any[]) => any>(
    callback: T,
    deps: React.DependencyList
  ): T => {
    const memoizedCallback = useCallback(callback, deps);
    return memoizedCallback;
  }, []);
  
  const createWeakRef = useCallback(<T extends object>(obj: T): WeakRef<T> => {
    return new WeakRef(obj);
  }, []);
  
  useEffect(() => {
    return () => {
      // 组件卸载时执行所有清理函数
      cleanupFunctions.current.forEach(cleanup => {
        try {
          cleanup();
        } catch (error) {
          console.error('Cleanup error:', error);
        }
      });
      cleanupFunctions.current = [];
    };
  }, []);
  
  return {
    addCleanup,
    createOptimizedCallback,
    createWeakRef
  };
}

// src/components/OptimizedComponent.tsx - 内存优化组件示例
import React, { memo, useMemo, useCallback } from 'react';
import { useMemoryOptimization } from '../hooks/useMemoryOptimization';

interface OptimizedComponentProps {
  data: any[];
  onItemClick: (item: any) => void;
  filter?: string;
}

export const OptimizedComponent = memo<OptimizedComponentProps>(({ 
  data, 
  onItemClick, 
  filter 
}) => {
  const { addCleanup, createOptimizedCallback } = useMemoryOptimization();
  
  // 使用 useMemo 缓存计算结果
  const filteredData = useMemo(() => {
    if (!filter) return data;
    
    return data.filter(item => 
      item.name?.toLowerCase().includes(filter.toLowerCase())
    );
  }, [data, filter]);
  
  // 使用 useCallback 缓存事件处理器
  const handleItemClick = createOptimizedCallback((item: any) => {
    onItemClick(item);
  }, [onItemClick]);
  
  // 虚拟滚动实现
  const VirtualizedList = useMemo(() => {
    const ITEM_HEIGHT = 50;
    const VISIBLE_ITEMS = 10;
    
    return ({ items }: { items: any[] }) => {
      const [scrollTop, setScrollTop] = React.useState(0);
      
      const startIndex = Math.floor(scrollTop / ITEM_HEIGHT);
      const endIndex = Math.min(
        startIndex + VISIBLE_ITEMS,
        items.length
      );
      
      const visibleItems = items.slice(startIndex, endIndex);
      
      const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
        setScrollTop(e.currentTarget.scrollTop);
      }, []);
      
      return (
        <div 
          style={{ height: VISIBLE_ITEMS * ITEM_HEIGHT, overflow: 'auto' }}
          onScroll={handleScroll}
        >
          <div style={{ height: items.length * ITEM_HEIGHT, position: 'relative' }}>
            {visibleItems.map((item, index) => (
              <div
                key={item.id}
                style={{
                  position: 'absolute',
                  top: (startIndex + index) * ITEM_HEIGHT,
                  height: ITEM_HEIGHT,
                  width: '100%'
                }}
                onClick={() => handleItemClick(item)}
              >
                {item.name}
              </div>
            ))}
          </div>
        </div>
      );
    };
  }, [handleItemClick]);
  
  return (
    <div className="optimized-component">
      <VirtualizedList items={filteredData} />
    </div>
  );
}, (prevProps, nextProps) => {
  // 自定义比较函数
  return (
    prevProps.data === nextProps.data &&
    prevProps.filter === nextProps.filter &&
    prevProps.onItemClick === nextProps.onItemClick
  );
});

OptimizedComponent.displayName = 'OptimizedComponent';

10.3 打包优化

10.3.1 Webpack 优化配置

// webpack.config.ts - Webpack 优化配置
import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import CompressionPlugin from 'compression-webpack-plugin';

const isProduction = process.env.NODE_ENV === 'production';
const shouldAnalyze = process.env.ANALYZE === 'true';

const config: webpack.Configuration = {
  mode: isProduction ? 'production' : 'development',
  
  entry: {
    main: './src/index.tsx',
    vendor: ['react', 'react-dom'] // 分离第三方库
  },
  
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: isProduction 
      ? '[name].[contenthash:8].js' 
      : '[name].js',
    chunkFilename: isProduction 
      ? '[name].[contenthash:8].chunk.js' 
      : '[name].chunk.js',
    clean: true,
    publicPath: '/'
  },
  
  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.jsx'],
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@/components': path.resolve(__dirname, 'src/components'),
      '@/utils': path.resolve(__dirname, 'src/utils')
    },
    // 优化模块解析
    modules: ['node_modules'],
    symlinks: false
  },
  
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'ts-loader',
            options: {
              transpileOnly: true, // 只转译,不做类型检查
              experimentalWatchApi: true,
              configFile: 'tsconfig.json'
            }
          }
        ]
      },
      {
        test: /\.css$/,
        use: [
          isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                auto: true,
                localIdentName: isProduction 
                  ? '[hash:base64:8]' 
                  : '[name]__[local]--[hash:base64:5]'
              }
            }
          },
          'postcss-loader'
        ]
      },
      {
        test: /\.(png|jpg|jpeg|gif|svg)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024 // 8KB
          }
        },
        generator: {
          filename: 'images/[name].[contenthash:8][ext]'
        }
      }
    ]
  },
  
  optimization: {
    minimize: isProduction,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: isProduction,
            drop_debugger: isProduction,
            pure_funcs: isProduction ? ['console.log', 'console.info'] : []
          },
          mangle: {
            safari10: true
          },
          format: {
            comments: false
          }
        },
        extractComments: false
      })
    ],
    
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
          priority: 10
        },
        common: {
          name: 'common',
          minChunks: 2,
          chunks: 'all',
          priority: 5,
          reuseExistingChunk: true
        },
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          chunks: 'all',
          priority: 20
        }
      }
    },
    
    runtimeChunk: {
      name: 'runtime'
    },
    
    moduleIds: 'deterministic',
    chunkIds: 'deterministic'
  },
  
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      minify: isProduction ? {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true
      } : false
    }),
    
    ...(isProduction ? [
      new MiniCssExtractPlugin({
        filename: '[name].[contenthash:8].css',
        chunkFilename: '[name].[contenthash:8].chunk.css'
      }),
      
      new CompressionPlugin({
        algorithm: 'gzip',
        test: /\.(js|css|html|svg)$/,
        threshold: 8192,
        minRatio: 0.8
      })
    ] : []),
    
    ...(shouldAnalyze ? [
      new BundleAnalyzerPlugin({
        analyzerMode: 'static',
        openAnalyzer: false,
        reportFilename: 'bundle-report.html'
      })
    ] : []),
    
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      __DEV__: !isProduction
    })
  ],
  
  devtool: isProduction ? 'source-map' : 'eval-source-map',
  
  devServer: {
    port: 3000,
    hot: true,
    historyApiFallback: true,
    compress: true,
    static: {
      directory: path.join(__dirname, 'public')
    }
  },
  
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename]
    }
  },
  
  stats: {
    colors: true,
    modules: false,
    children: false,
    chunks: false,
    chunkModules: false
  }
};

export default config;

10.3.2 Vite 优化配置

// vite.config.ts - Vite 优化配置
import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
import { visualizer } from 'rollup-plugin-visualizer';
import { compression } from 'vite-plugin-compression';
import { createHtmlPlugin } from 'vite-plugin-html';

export default defineConfig(({ command, mode }) => {
  const env = loadEnv(mode, process.cwd(), '');
  const isProduction = mode === 'production';
  
  return {
    plugins: [
      react({
        // React 优化配置
        babel: {
          plugins: isProduction ? [
            ['babel-plugin-react-remove-properties', { properties: ['data-testid'] }]
          ] : []
        }
      }),
      
      createHtmlPlugin({
        minify: isProduction,
        inject: {
          data: {
            title: 'TypeScript App',
            injectScript: isProduction ? '' : '<script src="/dev-tools.js"></script>'
          }
        }
      }),
      
      ...(isProduction ? [
        compression({
          algorithm: 'gzip',
          ext: '.gz'
        }),
        
        compression({
          algorithm: 'brotliCompress',
          ext: '.br'
        })
      ] : []),
      
      ...(env.ANALYZE ? [
        visualizer({
          filename: 'dist/stats.html',
          open: true,
          gzipSize: true,
          brotliSize: true
        })
      ] : [])
    ],
    
    resolve: {
      alias: {
        '@': resolve(__dirname, 'src'),
        '@/components': resolve(__dirname, 'src/components'),
        '@/utils': resolve(__dirname, 'src/utils')
      }
    },
    
    build: {
      target: 'es2020',
      minify: 'terser',
      terserOptions: {
        compress: {
          drop_console: isProduction,
          drop_debugger: isProduction,
          pure_funcs: isProduction ? ['console.log'] : []
        }
      },
      
      rollupOptions: {
        output: {
          manualChunks: {
            vendor: ['react', 'react-dom'],
            router: ['react-router-dom'],
            ui: ['@mui/material', '@emotion/react', '@emotion/styled']
          },
          
          chunkFileNames: (chunkInfo) => {
            const facadeModuleId = chunkInfo.facadeModuleId
              ? chunkInfo.facadeModuleId.split('/').pop()
              : 'chunk';
            return `js/${facadeModuleId}-[hash].js`;
          },
          
          assetFileNames: (assetInfo) => {
            const info = assetInfo.name!.split('.');
            const ext = info[info.length - 1];
            
            if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(ext)) {
              return `images/[name]-[hash][extname]`;
            }
            
            if (/css/i.test(ext)) {
              return `css/[name]-[hash][extname]`;
            }
            
            return `assets/[name]-[hash][extname]`;
          }
        }
      },
      
      chunkSizeWarningLimit: 1000,
      
      reportCompressedSize: false,
      
      sourcemap: isProduction ? 'hidden' : true
    },
    
    server: {
      port: 3000,
      host: true,
      open: true
    },
    
    preview: {
      port: 4173,
      host: true
    },
    
    optimizeDeps: {
      include: [
        'react',
        'react-dom',
        'react-router-dom'
      ],
      exclude: [
        'some-large-optional-dependency'
      ]
    },
    
    define: {
      __DEV__: !isProduction,
      'process.env.NODE_ENV': JSON.stringify(mode)
    }
  };
});

本章练习

  1. 编译性能优化

    • 配置增量编译和项目引用
    • 分析编译性能瓶颈
    • 优化 tsconfig.json 配置
  2. 运行时优化

    • 实现代码分割和懒加载
    • 优化组件渲染性能
    • 实现内存管理策略
  3. 打包优化

    • 配置 Webpack 或 Vite 优化
    • 分析打包产物大小
    • 实现资源压缩和缓存
  4. 性能监控

    • 实现性能指标收集
    • 建立性能监控体系
    • 制定性能优化策略

本章总结

本章介绍了 TypeScript 项目的全面性能优化策略:

  1. 编译性能:通过配置优化、增量编译和项目引用提升编译速度
  2. 运行时性能:通过类型优化、代码分割和内存管理提升运行效率
  3. 打包优化:通过构建工具配置和资源优化减少包体积
  4. 监控体系:建立完整的性能监控和分析体系

性能优化是一个持续的过程,需要根据项目特点和用户需求制定合适的优化策略。