Skip to content

性能与可维护性考量

AI 生成的代码能够快速实现功能,但在性能和可维护性上存在一些系统性的盲区。本文总结最常见的问题模式,并提供检测和修复策略。

AI 代码的性能盲区

AI 模型在生成代码时,优先确保功能正确,而对运行时的资源消耗关注有限。以下是最常见的性能陷阱。


数据库 N+1 查询问题

这是 AI 生成后端代码时最常见的性能问题,在 ORM 使用场景中尤为突出:

typescript
// 危险:N+1 查询(AI 常见产出)
// 对于 100 个订单,会执行 1 + 100 = 101 条 SQL
async function getOrdersWithUsers(orderIds: number[]) {
  const orders = await Order.findAll({ where: { id: orderIds } }); // 1 次查询
  for (const order of orders) {
    order.user = await User.findByPk(order.userId); // N 次查询
  }
  return orders;
}

// 正确:使用 JOIN 或预加载,2 次查询解决问题
async function getOrdersWithUsers(orderIds: number[]) {
  return Order.findAll({
    where: { id: orderIds },
    include: [{ model: User, attributes: ['id', 'name', 'email'] }],
  });
}

GraphQL 场景下的 N+1 解决方案(DataLoader):

typescript
// dataloader.ts
import DataLoader from 'dataloader';

export const userLoader = new DataLoader<number, User>(async (userIds) => {
  const users = await User.findAll({ where: { id: userIds } });
  // 保证返回顺序与输入 keys 一致
  return userIds.map((id) => users.find((u) => u.id === id) ?? null);
});

// 在 resolver 中使用
const userResolver = {
  Order: {
    user: (order: Order) => userLoader.load(order.userId), // 自动批量
  },
};

内存泄漏

AI 生成的代码有时会遗漏清理操作,导致内存随时间不断增长:

typescript
// 危险:未清理的定时器(React 组件)
function LiveCounter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const interval = setInterval(() => {
      setCount((c) => c + 1);
    }, 1000);
    // AI 常常遗漏这一行:
    // return () => clearInterval(interval);
  }, []);
  return <span>{count}</span>;
}

// 正确:返回清理函数
function LiveCounter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const interval = setInterval(() => setCount((c) => c + 1), 1000);
    return () => clearInterval(interval);  // 组件卸载时清理
  }, []);
  return <span>{count}</span>;
}
typescript
// 危险:Node.js 中未移除的事件监听器
class DataProcessor extends EventEmitter {
  process(stream: Readable) {
    stream.on('data', this.handleData.bind(this));  // 每次调用都添加新监听器
    // 缺少 stream.off 或 { once: true }
  }
}

// 正确:使用 once 或手动移除
class DataProcessor extends EventEmitter {
  process(stream: Readable) {
    const handler = this.handleData.bind(this);
    stream.on('data', handler);
    stream.once('end', () => stream.off('data', handler));
  }
}

React 不必要的重渲染

AI 生成的 React 组件常常缺少记忆化优化:

tsx
// 问题:每次父组件渲染,子组件都重新渲染
function ParentComponent() {
  const [count, setCount] = useState(0);

  // 每次渲染都创建新函数引用,导致 ExpensiveChild 重渲染
  const handleClick = () => doSomething();

  // 每次渲染都创建新对象,即使内容相同
  const config = { theme: 'dark', size: 'large' };

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>+1</button>
      <ExpensiveChild onClick={handleClick} config={config} />
    </>
  );
}

// 优化版本
function ParentComponent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => doSomething(), []);
  const config = useMemo(() => ({ theme: 'dark', size: 'large' }), []);

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>+1</button>
      <ExpensiveChild onClick={handleClick} config={config} />
    </>
  );
}

// ExpensiveChild 也需要包裹 memo 才能避免重渲染
const ExpensiveChild = memo(({ onClick, config }) => {
  return <div>...</div>;
});

重渲染检测工具:

bash
# 使用 React DevTools Profiler 录制渲染
# 或安装 why-did-you-render 进行运行时检测
npm install -D @welldone-software/why-did-you-render
typescript
// src/wdyr.ts(仅在开发环境引入)
import React from 'react';
import whyDidYouRender from '@welldone-software/why-did-you-render';

if (process.env.NODE_ENV === 'development') {
  whyDidYouRender(React, { trackAllPureComponents: true });
}

未优化的循环与算法

AI 倾向于生成语义清晰但效率较低的实现:

typescript
// 低效:O(n²) 复杂度
function findDuplicates(items: string[]): string[] {
  const duplicates: string[] = [];
  for (let i = 0; i < items.length; i++) {
    for (let j = i + 1; j < items.length; j++) {
      if (items[i] === items[j] && !duplicates.includes(items[i])) {
        duplicates.push(items[i]);
      }
    }
  }
  return duplicates;
}

// 优化:O(n) 复杂度
function findDuplicates(items: string[]): string[] {
  const seen = new Set<string>();
  const duplicates = new Set<string>();
  for (const item of items) {
    if (seen.has(item)) duplicates.add(item);
    else seen.add(item);
  }
  return [...duplicates];
}
typescript
// 低效:多次遍历数组
const activeUsers = users.filter(u => u.isActive);
const adminUsers = users.filter(u => u.role === 'admin');
const activeAdmins = users.filter(u => u.isActive && u.role === 'admin');

// 优化:一次遍历
const { active, admins, activeAdmins } = users.reduce(
  (acc, user) => {
    if (user.isActive) acc.active.push(user);
    if (user.role === 'admin') acc.admins.push(user);
    if (user.isActive && user.role === 'admin') acc.activeAdmins.push(user);
    return acc;
  },
  { active: [], admins: [], activeAdmins: [] } as Record<string, User[]>
);

可维护性问题

过度抽象

AI 有时会生成过度工程化的代码,引入不必要的抽象层:

typescript
// AI 生成的过度抽象(实际项目中常见)
class UserRepositoryFactoryProviderAbstractService {
  createRepository(type: RepositoryType): AbstractUserRepository {
    return RepositoryRegistry.getInstance().resolve(type);
  }
}

// 实际需要的可能只是:
async function findUserById(id: number): Promise<User | null> {
  return db.query('SELECT * FROM users WHERE id = ?', [id]);
}

评估抽象是否必要的问题:

  • 这个抽象是否有两个以上不同的实现?
  • 如果删除这一层,代码会变得更难理解吗?
  • 这个模式在团队成员中是否广为人知?

死代码检测

bash
# 使用 ts-prune 检测未使用的导出
npx ts-prune

# 使用 knip 进行更全面的死代码检测
npx knip

# eslint 规则检测未使用的变量(已在 code-quality 配置中包含)
# @typescript-eslint/no-unused-vars: error

不一致的代码模式

AI 在同一项目中可能产出风格不一致的代码:

typescript
// 同一代码库中出现两种数据获取模式(应统一)
// 模式 A:在 useEffect 中 fetch
useEffect(() => {
  fetch('/api/users').then(r => r.json()).then(setUsers);
}, []);

// 模式 B:使用 React Query(项目已引入)
const { data: users } = useQuery({ queryKey: ['users'], queryFn: fetchUsers });

在 Code Review 中,应要求 AI 生成的代码遵循项目已有的数据获取模式,而不是引入新的方式。


性能 Code Review 检查清单

检查项典型症状工具
N+1 查询日志中出现大量相似 SQLORM 的 logging 模式
未清理的副作用长时间运行后内存持续上涨Chrome DevTools Memory
不必要的重渲染输入时整页闪烁React DevTools Profiler
未使用 useMemo/useCallbackProps 是对象/函数且子组件未用 memowhy-did-you-render
同步阻塞主线程界面卡顿、ANRPerformance tab
未分页的大列表渲染 1000+ 条 DOM 节点Lighthouse
重复的 API 请求同一数据多次请求Network tab

监控建议

性能问题在生产环境中才会完全暴露,需要持续监控:

typescript
// 前端核心 Web 指标上报
import { onCLS, onFID, onLCP, onTTFB } from 'web-vitals';

function sendToAnalytics(metric: Metric) {
  // 上报到监控平台(如 Datadog, Grafana)
  navigator.sendBeacon('/api/metrics', JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,  // 'good' | 'needs-improvement' | 'poor'
    page: location.pathname,
  }));
}

onCLS(sendToAnalytics);   // Cumulative Layout Shift
onFID(sendToAnalytics);   // First Input Delay
onLCP(sendToAnalytics);   // Largest Contentful Paint
onTTFB(sendToAnalytics);  // Time to First Byte

推荐监控告警阈值:

指标良好需关注告警阈值
LCP< 2.5s2.5-4s> 4s
FID / INP< 100ms100-300ms> 300ms
CLS< 0.10.1-0.25> 0.25
API P99 响应< 500ms500ms-1s> 1s
错误率< 0.1%0.1-1%> 1%

性能优化应以数据为驱动,先通过监控和 profiling 确认瓶颈所在,再有针对性地重构 AI 生成的代码,避免在非热点路径上过早优化。