拯救 AI 生成的烂代码:Vibe Coding 后的重构指南
一、当“能跑就行”变成“跑着跑着就崩了”
2025 年以来,Vibe Coding 已成为开发圈最炙手轻快的关键词。开发者只需用自然语言描述需求,AI 就能在几分钟内生成一个功能完整的应用——从贪吃蛇游戏到数据聚合平台,效率提升令人目眩。
但“能跑”不等于“跑得好”。Databricks 的 AI 红队研究发现,Vibe Coding 产出的代码往往隐藏着严重的安全漏洞和性能隐患——从 Python 的 pickle 反序列化漏洞到 C/C++ 的内存越界读写,这些问题在“看起来能工作”的表象下悄然滋生。更令人担忧的是,Wiz Research 对百万级 AI 生成应用的扫描显示,每 5 个组织中就有 1 个因 vibe-coded 应用暴露了敏感数据。
本文将结合真实案例,剖析 AI 生成代码中最常见的三类陷阱——性能瓶颈、内存泄露、逻辑漏洞——并展示如何借助 Rust 工具链(Biome、Rspack)建立工程化约束,让“氛围代码”走向生产级。
二、AI 生成代码的三宗罪
2.1 性能陷阱:你以为的简洁,其实是灾难
来看一个看似无害的 React 组件——这是用提示词“帮我写一个用户列表页面,支持搜索和排序”生成的典型代码:
function UserList({ users }) {
const [searchTerm, setSearchTerm] = useState('');
const [sortField, setSortField] = useState('name');
// 🔴 性能陷阱:每次渲染都重新计算
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase())
);
// 🔴 二次遍历:排序又一次全量遍历
const sortedUsers = [...filteredUsers].sort((a, b) => {
if (sortField === 'name') {
return a.name.localeCompare(b.name);
}
return a.age - b.age;
});
return (
<div>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{sortedUsers.map(user => <UserCard key={user.id} user={user} />)}
</div>
);
}
问题分析:
- 无记忆化的重复计算:每次键盘输入都触发
filter+sort双遍历,1000 条数据下每次渲染耗时 5-8ms,打字时帧率骤降 - 不必要的数组拷贝:
[...filteredUsers]每次创建新数组,增加垃圾回收压力 - 重复的字符串操作:
toLowerCase()在每次过滤时对同一用户重复执行
修复方案:
function UserList({ users }) {
const [searchTerm, setSearchTerm] = useState('');
const [sortField, setSortField] = useState('name');
// ✅ 使用 useMemo 缓存计算结果
const normalizedSearchTerm = useMemo(
() => searchTerm.toLowerCase(),
[searchTerm]
);
const processedUsers = useMemo(() => {
// ✅ 一次遍历完成过滤和排序准备
const filtered = users.filter(user =>
user.name.toLowerCase().includes(normalizedSearchTerm)
);
// ✅ 使用 Intl.Collator 提升排序性能
const collator = new Intl.Collator('zh-CN');
return filtered.sort((a, b) => {
if (sortField === 'name') {
return collator.compare(a.name, b.name);
}
return a.age - b.age;
});
}, [users, normalizedSearchTerm, sortField]);
// ✅ 使用 useDeferredValue 优化输入响应
const deferredUsers = useDeferredValue(processedUsers);
return (
<div>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{deferredUsers.map(user => <UserCard key={user.id} user={user} />)}
</div>
);
}
2.2 内存泄露:闭包与事件监听的幽灵
这是一个 AI 生成的 WebSocket 聊天组件:
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const ws = new WebSocket(`wss://api.example.com/chat/${roomId}`);
ws.onmessage = (event) => {
const newMessage = JSON.parse(event.data);
setMessages(prev => [...prev, newMessage]);
};
// 🔴 内存泄露:没有清理 WebSocket
// 🔴 竞态条件:roomId 变化时旧连接未关闭
}, [roomId]);
return <MessageList messages={messages} />;
}
问题分析:
- WebSocket 连接未在组件卸载时关闭,每次
roomId变化都创建新连接而旧连接继续存活 onmessage闭包持有旧 state 引用,可能导致内存无法回收- 网络断线后无重连机制,用户体验差
修复方案:
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
const wsRef = useRef(null);
useEffect(() => {
let isMounted = true;
let reconnectTimer = null;
const connect = () => {
const ws = new WebSocket(`wss://api.example.com/chat/${roomId}`);
wsRef.current = ws;
ws.onmessage = (event) => {
if (isMounted) {
const newMessage = JSON.parse(event.data);
setMessages(prev => [...prev, newMessage]);
}
};
ws.onclose = () => {
if (isMounted) {
reconnectTimer = setTimeout(connect, 3000);
}
};
};
connect();
// ✅ 清理函数:关闭连接、取消重连、防止内存泄露
return () => {
isMounted = false;
clearTimeout(reconnectTimer);
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.close();
}
};
}, [roomId]);
return <MessageList messages={messages} />;
}
2.3 逻辑漏洞:权限校验的假安全感
这是 36 氪报道的一个真实案例——开发者用 AI 三天做出数据聚合网站,结果两天内被白帽黑客攻破两次:
// 🔴 漏洞:前端隐藏了注册入口,但后端 API 仍然开放
// 攻击者只需直接调用 API 即可注册账号
// 前端代码 - 注册按钮被注释掉
{/* <button onClick={handleSignUp}>注册</button> */}
// 后端 API - 仍然可以接受任意请求
app.post('/api/auth/signup', async (req, res) => {
const { email, password } = req.body;
// 🔴 没有任何来源校验
const user = await db.users.create({ email, password });
res.json({ user });
});
更隐蔽的漏洞——数据库视图权限绕过:
-- 开发者创建了一个视图来隐藏敏感字段
CREATE VIEW public_user_data AS
SELECT id, name, avatar_url FROM users;
-- 🔴 漏洞:视图默认以创建者权限运行,行级安全策略被绕过
-- 攻击者通过视图获得了完整的增删改权限
修复方案:
-- ✅ 显式指定 SECURITY INVOKER,强制执行行级安全
CREATE VIEW public_user_data
WITH (security_invoker = true) AS
SELECT id, name, avatar_url FROM users
WHERE deleted_at IS NULL;
-- ✅ 配合行级安全策略
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY users_select_policy ON users
FOR SELECT
USING (is_public = true OR auth.uid() = id);
三、工程化约束:让 Rust 工具链成为代码守门人
靠人工代码审查发现所有问题不现实。我们需要自动化工具在代码合入前拦截问题。
3.1 Biome:一秒扫描,零配置起步
Biome 是用 Rust 编写的 JavaScript/TypeScript 工具链,速度是 ESLint 的 25-50 倍。它集成了代码检查、格式化、导入排序三大功能。
安装与基础配置:
npm install --save-dev @biomejs/biome
初始化配置 biome.json:
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"include": ["src/**/*.{js,jsx,ts,tsx}"],
"ignore": ["node_modules", "dist", "coverage"]
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"useExhaustiveDependencies": "error",
"noUnusedVariables": "error"
},
"suspicious": {
"noArrayIndexKey": "error",
"noAssignInExpressions": "error"
},
"performance": {
"noAccumulatingSpread": "error",
"noDelete": "error"
},
"security": {
"noDangerouslySetInnerHtml": "error"
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "es5"
}
}
}
Git 钩子集成(推荐使用 Husky 或 Lefthook):
# .husky/pre-commit
npx biome check --staged --no-errors-on-unmatched
3.2 Rspack:构建时性能画像
Rspack 是字节跳动开源的 Rust 打包工具,与 Webpack 生态兼容但快 10 倍。它内置了精细的性能分析能力。
开启性能分析:
# 生成详细的构建耗时报告
RSPACK_PROFILE=ALL rspack build
命令执行后生成 trace.json,上传到 ui.perfetto.dev 即可可视化分析:
- 哪个加载器耗时最长(babel-loader 通常是罪魁祸首)
- 模块解析瓶颈在哪
- 哪些插件拖慢了整体构建
常见性能优化对照表:
| 原工具 | 问题 | Rspack 替代方案 |
|---|---|---|
| babel-loader | 单线程 JavaScript 编译 | builtin:swc-loader(Rust) |
| terser-webpack-plugin | 压缩慢 | SwcJsMinimizerRspackPlugin |
| postcss-loader | CSS 处理慢 | builtin:lightningcss-loader |
| css-minimizer-webpack-plugin | 压缩慢 | LightningCssMinimizerRspackPlugin |
配置示例:
// rspack.config.js
export default {
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
// ✅ 使用 Rust 版 SWC 替代 Babel
loader: 'builtin:swc-loader',
options: {
jsc: { parser: { syntax: 'typescript', tsx: true } }
}
},
{
test: /\.css$/,
// ✅ 使用 Lightning CSS 替代 PostCSS
use: ['builtin:lightningcss-loader']
}
]
},
optimization: {
minimizer: [
new rspack.SwcJsMinimizerRspackPlugin(),
new rspack.LightningCssMinimizerRspackPlugin()
]
}
};
3.3 持续集成流水线:自动化质量门禁
将 Biome 和 Rspack 检查集成到持续集成,确保问题在合入前被发现:
# .github/workflows/quality.yml
name: 代码质量检查
on: [pull_request]
jobs:
代码检查与构建:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 设置 Node 环境
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 安装依赖
run: npm ci
- name: Biome 检查
run: npx biome ci --reporter=github
- name: 带性能分析的构建
run: RSPACK_PROFILE=ALL npm run build
- name: 上传构建追踪文件
uses: actions/upload-artifact@v4
if: always()
with:
name: rspack-profile
path: .rspack-profile-*
四、提示词层面的事前防御
除了工具链约束,在提示词阶段就能规避大量问题。Databricks 的研究表明,自反思策略可减少 48%-50% 的漏洞代码生成。
4.1 安全导向的系统提示词
在每次会话开始时附加:
编写代码时请将安全性和性能作为首要考量:
1. 内存安全:务必清理事件监听器、定时器和订阅。
2. 输入验证:在使用前对所有用户输入进行净化和验证。
3. 性能优化:对开销大的计算使用记忆化(useMemo、useCallback)。
4. 依赖数组:确保 useEffect/useCallback 的依赖项完整。
5. 密钥管理:禁止在客户端代码中硬编码 API 密钥或凭证。
6. 身份验证:所有认证逻辑必须位于服务端;客户端仅发送凭证。
4.2 自反思审查提示词
生成代码后,追加以下审查指令:
请检查上述代码是否存在以下问题:
- useEffect 或事件监听器中缺失清理逻辑
- 未记忆化的高开销计算
- 闭包导致的潜在内存泄露
- 客户端代码中的密钥或硬编码凭证
- React Hooks 中不正确的依赖数组
仅返回修复后的代码,并用注释说明修改原因。
4.3 语言特定的安全提示词
针对 TypeScript/JavaScript 项目:
TypeScript 特定安全要求:
- 使用 `import type` 进行仅类型导入
- 避免使用 `any`;优先使用 `unknown` 配合类型守卫
- 在 tsconfig 中启用 `strictNullChecks`
- 禁止在未使用 DOMPurify 的情况下使用 `dangerouslySetInnerHTML`
- 使用 Zod 或类似的模式校验库验证 API 响应
五、实战案例:重构一个 AI 生成的数据看板
让我们完整走一遍流程——这是用提示词“做一个数据分析看板,展示图表和表格”生成的代码:
// 🔴 原始 AI 生成代码
function Dashboard() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(setData)
.finally(() => setLoading(false));
}); // 🔴 缺少依赖数组,无限循环
const chartData = {
labels: data.map(d => d.date),
datasets: [{
data: data.map(d => d.value)
}]
}; // 🔴 每次渲染重新计算
return (
<div>
{loading && <Spinner />}
<Line data={chartData} />
<table>
{data.map((row, i) => ( // 🔴 用索引作 key
<tr key={i}>
<td>{row.date}</td>
<td dangerouslySetInnerHTML={{__html: row.value}} /> // 🔴 跨站脚本风险
</tr>
))}
</table>
</div>
);
}
修复后:
import { useMemo, useEffect, useState } from 'react';
import DOMPurify from 'dompurify';
function Dashboard() {
const [data, setData] = useState<DataRow[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') console.error(err);
})
.finally(() => setLoading(false));
// ✅ 清理函数,避免内存泄露
return () => controller.abort();
}, []); // ✅ 正确的依赖数组
// ✅ 缓存图表数据计算
const chartData = useMemo(() => ({
labels: data.map(d => d.date),
datasets: [{
label: '指标',
data: data.map(d => d.value)
}]
}), [data]);
return (
<div>
{loading && <Spinner />}
<Line data={chartData} />
<table>
<tbody>
{data.map(row => (
// ✅ 使用稳定的 id 作为 key
<tr key={row.id}>
<td>{row.date}</td>
{/* ✅ 使用 DOMPurify 防止跨站脚本攻击 */}
<td>{DOMPurify.sanitize(row.value)}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
运行质量扫描:
# Biome 检查通过
$ npx biome check src/Dashboard.tsx
已检查 1 个文件,耗时 12ms。无需修复。
# Rspack 构建耗时从 4.2s 降至 1.8s
$ RSPACK_PROFILE=ALL rspack build
✅ 构建完成,耗时 1.82s
六、总结
Vibe Coding 不是洪水猛兽——它只是把“写得快”和“写得好”的矛盾暴露得更赤裸。解决问题的关键不在于拒绝 AI,而在于建立一套适配 AI 时代的工程纪律:
| 阶段 | 手段 | 工具 |
|---|---|---|
| 编写时 | 安全导向的提示词、自反思审查 | 系统提示词模板 |
| 提交前 | Git 钩子自动扫描 | Biome(Rust) |
| 构建时 | 性能画像、构建优化 | Rspack profile(Rust) |
| 持续集成中 | 自动化质量门禁 | Biome CI + Rspack |
三个核心原则值得记住:
- 信任但验证:AI 生成的代码能跑,不等于没问题
- 自动化优先:把检查逻辑写进工具,而非依赖人工记忆
- Rust 是秘密武器:Biome 和 Rspack 用 Rust 提供了 Web 生态久违的速度和可靠性
最后,送你一个可以直接复制使用的 .cursorrules 或 .clinerules 配置:
# AI 编码规则
安全性:
- 所有 API 密钥必须从环境变量读取,禁止硬编码
- 客户端代码不得包含任何形式的权限判断逻辑
- 所有用户输入必须经过 DOMPurify 或后端校验
性能:
- useEffect 必须包含依赖数组
- 复杂计算必须使用 useMemo 包裹
- 传递给子组件的回调必须使用 useCallback
React规范:
- 列表渲染必须使用稳定的 key(优先 id,禁止 index)
- 事件监听器必须在 useEffect 返回的清理函数中移除
Vibe Coding 让想法快速落地,而工程化工具链让落地后的代码走得更远。两者结合,才是 AI 编程的正确打开方式。
602

被折叠的 条评论
为什么被折叠?



