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)
}
};
});
本章练习
编译性能优化:
- 配置增量编译和项目引用
- 分析编译性能瓶颈
- 优化 tsconfig.json 配置
运行时优化:
- 实现代码分割和懒加载
- 优化组件渲染性能
- 实现内存管理策略
打包优化:
- 配置 Webpack 或 Vite 优化
- 分析打包产物大小
- 实现资源压缩和缓存
性能监控:
- 实现性能指标收集
- 建立性能监控体系
- 制定性能优化策略
本章总结
本章介绍了 TypeScript 项目的全面性能优化策略:
- 编译性能:通过配置优化、增量编译和项目引用提升编译速度
- 运行时性能:通过类型优化、代码分割和内存管理提升运行效率
- 打包优化:通过构建工具配置和资源优化减少包体积
- 监控体系:建立完整的性能监控和分析体系
性能优化是一个持续的过程,需要根据项目特点和用户需求制定合适的优化策略。