🤖 AI 生成前端代码的 5 个典型翻车现场及修复指南

问题场景

"我用 Cursor 生成了一段 React 组件,本地跑得好好的,上线就报错。查了半天,AI 把 useEffect 的依赖数组写错了,造成死循环。" "Copilot 帮我写了一个表格组件,看起来没问题,结果客户反馈表格不更新数据......"

如果你也在用 AI 辅助写前端代码,多少都遇到过类似的"AI 幻觉 bug"。本文整理了 5 个最常踩的坑,以及如何快速定位和修复它们。


坑 1:React useEffect 依赖数组遗漏

现象

AI 生成的代码:

tsx 复制代码
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(setUser);
  }, []); // ⚠️ AI 漏掉了 userId

页面首次加载正常,切换用户后数据不变。React 严格模式开发下跑了两遍,上线后就暴露了。

原因

AI 经常把 [] 当成"只运行一次"的默认写法,忽略了外部依赖。特别是当提示词中只提了"获取用户数据",没有说"随 userId 变化重新请求"时。

修复

tsx 复制代码
useEffect(() => {
  fetch(`/api/users/${userId}`)
    .then(res => res.json())
    .then(setUser);
}, [userId]); // ✅ 补上 userId

进阶方案 :用 useDatareact-query 替代原始 useEffect,AI 也很容易写出带缓存的请求方案,比手动管理依赖更安全。


坑 2:CSS 类名冲突(Tailwind + 自定义样式混用)

现象

AI 生成的组件:

tsx 复制代码
<div className="flex items-center card-container">

Tailwind 的 flex items-center 正常工作,但 card-container 这个 CSS Module 的类名没有被自动注入了------因为 AI 把 CSS Modules 的 styles.cardContainer 写成了字符串。

原因

AI 在同一个组件中混用了全局 CSSTailwindCSS Modules 三种方案,且没有按项目规范统一。它只是根据见过的各种代码推断出"这样写应该可以",实际上在 CSS Modules 项目里,className="card-container" 不会被转换成哈希类名。

修复

tsx 复制代码
// ✅ 统一使用 CSS Modules
import styles from './UserProfile.module.css';

<div className={`${styles.cardContainer} flex items-center`}>

通用原则

  • 项目用 Tailwind → 告诉 AI "只用 Tailwind,不许写自定义类"
  • 项目用 CSS Modules → className={styles.xxx}
  • 项目用 CSS-in-JS → 明确指定 styled-components 或 Emotion

坑 3:TypeScript 类型断言滥用

现象

AI 遇到类型报错时,最喜欢用的一招就是 as any

tsx 复制代码
const data = response.data as any; // 🤦 城门失火,殃及池鱼
const items = data.items.map((item: any) => ({
  ...item,
  label: item.name ?? '',
}));

整段代码变成了无类型保护 的状态,后续任何人在此基础上开发,IDE 提示全变成 any,等于 TypeScript 白用了。

原因

AI 优化的目标函数是"代码能跑",而不是"类型安全"。它检测到类型错误时,会优先选择修复成本最低的方案------as any 是最便宜的,但也是危害最大的。

修复

tsx 复制代码
// ✅ 正确方案:定义接口 + 类型守卫
interface ApiResponse<T> {
  code: number;
  data: T;
}

interface Item {
  id: string;
  name: string;
}

function isItemArray(data: unknown): data is Item[] {
  return Array.isArray(data) && data.every(
    item => typeof (item as Item).id === 'string'
  );
}

// 使用时
const result: ApiResponse<Item[]> = response.data;
if (!isItemArray(result.data)) throw new Error('数据格式异常');

提示词技巧:在 prompt 中加上 "严禁使用 as any,必须定义完整类型"。


坑 4:状态更新依赖旧值

现象

AI 写的一个计数累加组件:

tsx 复制代码
function Counter() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    for (let i = 0; i < 5; i++) {
      setCount(count + i); // ⚠️ 每次都是 0 + i
    }
  };

点击后 count 变成了 4,而不是期望的 0+1+2+3+4 = 10。因为 React 的状态更新是异步批处理 的,循环中的每次 setCount 都基于同一个 count 值。

原因

AI 不理解 React 状态更新的闭包陷阱批处理机制 。它写出的是直觉上的"每次加 i",但实际执行的是"连续 5 次 setCount(0+i)"。

修复

tsx 复制代码
// ✅ 使用函数式更新
const handleClick = () => {
  for (let i = 0; i < 5; i++) {
    setCount(prev => prev + i);
  }
};

同理也适用于 useReducer 和 Vue 的 ref/reactive------AI 经常写出直接读取最新值而非通过函数式更新的代码。


坑 5:错误处理------"只要不报错就算成功"

现象

AI 生成的 API 调用代码:

tsx 复制代码
async function loadData() {
  try {
    const res = await fetch('/api/data');
    const json = await res.json();
    setData(json);
  } catch (e) {
    console.error(e);
  }
}

看起来没问题?坑在中间:res.json() 可以抛出 JSON 解析错误、网络错误可以被 catch,但是 HTTP 400/500 不会触发 catch,会被当作成功处理!

原因

AI 的"认知盲区":HTTP 错误码(4xx/5xx)不会让 fetch 抛出异常,只有网络断连才会。AI 训练数据中大量包含 fetch → then → catch 的简单写法,但很少强调 res.ok 检查。

修复

tsx 复制代码
async function loadData() {
  try {
    const res = await fetch('/api/data');

    // ✅ 必须手动检查 HTTP 状态
    if (!res.ok) {
      throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    }

    const json = await res.json();

    // ✅ 再检查业务状态码
    if (json.code !== 0) {
      throw new Error(json.message || '业务异常');
    }

    setData(json.data);
  } catch (e) {
    // ✅ 给用户友好的反馈,不是只打 log
    toast.error(e instanceof Error ? e.message : '请求失败,请重试');
  }
}

要点总结

坑位 AI 的典型错误 修复方案 提示词可预防
useEffect 依赖 漏掉依赖项或乱写 [] 补全依赖 / 改用 SWR ✅ "考虑所有响应式依赖"
CSS 冲突 混用多种样式方案 统一规范、用 CSS Modules ✅ "严格遵循项目 CSS 规范"
类型安全 滥用 as any 定义接口 / 类型守卫 ✅ "严禁使用 any"
状态更新 依赖旧值、闭包陷阱 函数式更新 / useReducer 需要人工审查
错误处理 只 catch 不检查状态码 检查 res.ok + 状态码 ✅ "处理 4xx/5xx"

核心建议

  1. AI 写的代码 必须逐行 review,特别是状态管理和异步逻辑
  2. 写提示词时明确约束("不用 any"、"检查 HTTP 状态码")
  3. 配合 ESLint + TypeScript strict 模式 自动拦截低级错误
  4. AI 擅长写样板代码 ,但业务逻辑和边界情况请自己把关

AI 是增速器,不是代驾。越是关键时刻,越要自己握住方向盘 🚗

相关推荐
IT_陈寒2 小时前
SpringBoot自动装配坑了我一天,原来问题出在这
前端·人工智能·后端
索西引擎3 小时前
【React】key 属性:协调算法中的元素标识机制与最佳实践
前端·javascript·react.js
只会cv的前端攻城狮3 小时前
动态组件架构设计:从配置到事件驱动全链路解析
前端·vue.js
飞天狗3 小时前
Next.js 16 App Router 实战:服务端组件与流式渲染
前端·next.js
OpenTiny社区3 小时前
TinyEngine 2.11版本发布:AI 进一步融入画布,Vue 工程可一键转DSL
前端·vue.js·github
小雪_Snow3 小时前
JavaScript 中的三种常用事件
开发语言·前端·javascript
Hyyy4 小时前
Electron-Vite是什么,和Vite有什么区别
前端·面试
蓝胖子酱4 小时前
在 Electron 中用 electron-store 实现加密存储与自定义格式文件
前端·javascript·数据库
虚焦像素_4 小时前
海康 Web 插件版接入指南:用法与注意事项
前端