接手AI生成的前端代码越多,我越发现一个规律:组件复用的关键不在“复用”本身,而在你能不能从一堆看似已经生成的代码里,快速识别出那条“可抽离的边界”。用AI写页面确实快,但如果不做这层拆解,后期改需求的时候,你会面对一堆几乎一模一样的卡片、弹窗和表格,改一个还好,同时改十几个,心态直接崩塌。
这篇文章不是讲组件库怎么搭,也不是给你一套规范文档,而是聊我在实际项目里,怎么从AI批量生成的代码中找出复用点、抽成组件、再反过来把这些规律喂回给AI的过程。适合刚接触AI辅助开发的前端,也适合那些觉得“AI代码结构太乱、没法维护”的人。
1. 为什么AI生成的代码天生就带“重复基因”
先说结论:AI生成代码的重复,不是它蠢,而是它在用“最保守的策略”对抗需求的不确定性。理解这一点,你就能容忍它的重复,并且有方向地去拆解。
我自己调试过不少AI生成的前端页面,发现它输出代码时有个底层逻辑:能局部确定的,绝不全局抽象。你让它生成一个用户信息卡片,它会老老实实把头像、昵称、标签、操作按钮全部写在同一个div里;你让它再生成一个订单卡片,它还是这么做。两段代码放在一起,80%的结构几乎一致,只是字段名和数据绑定的位置不同。
这背后的原因是,AI在生成时并不知道你的真实数据模型,更不知道你未来会不会在卡片里加一个“置顶”操作。它选择了最稳妥的写法——把变量直接铺在模板上,变量变了、页面变了,但代码骨架不动。这种策略单次生成没问题,可是当页面数量多了、业务字段复杂了,问题就暴露了:重复的DOM结构、重复的样式声明、重复的事件绑定逻辑,让后续任何一次改动都变成体力活。
所以在我的工作流里,AI生成了第一批页面之后,我不会急着继续往下生成,而是先做一次“去重分析”。说白了,就是站在全局视角,把AI当成一个产出“原材料”的实习生,我得把这些原材料清洗、归类,然后重构成半成品件。
这背后还有一个更值得注意的现象:AI生成代码时对命名非常“短视”。它不可能像团队里经验丰富的老前端一样,想到一个UserCard组件将来会被三个页面共用,于是提前把props设计好;它只会盯着当前这个需求,生成一个独用的、一次性的DOM片段。所以“复用规律”这四个字,不是在AI代码里找现成的,而是需要你去提炼。
提示:拿到AI生成的代码,第一步不是跑起来看效果,而是全局扫描“DOM结构的重复率”。我用的土办法是搜索相似的开头标签,比如
<div class="card"出现次数超过3次,这里大概率就是第一个该抽的组件。
2. 先从AI代码里识别“可抽离”的三个信号
很多人抽组件失败,不是因为不会写React或Vue,而是抽错了对象。有的代码明明该拆,你硬是合并;有的代码虽然重复,但抽出来反而更乱。我在反复处理AI生成代码之后,总结出三个比较靠谱的识别信号。
2.1 看数据结构,不看视觉结构
AI生成的代码重复最明显的往往在视觉层,同一个卡片、同一个按钮、同一个表单行。但真正适合抽成组件的,不是视觉相同,而是数据结构相同。
举个例子,AI给你生成了“用户信息区块”和“商品信息区块”,两者看起来都是“头像+标题+描述+标签”,长得挺像。但用户区块的数据结构是{ name, avatar, roles },商品区块是{ title, cover, price, tags },字段完全不同,强行抽成一个通用组件,会导致props里塞一堆可选字段,写着写着就变成万能组件,谁也看不懂。这时候反而应该保留两套代码,最多把其中真正共用的“头像+标题”这一小段再单独提出来。
正确做法是:先把AI代码里的数据结构列出来,对照看哪些实体的“字段集合”高度重合。比如多个AI生成的表格组件,都是“列表+分页+加载态”,那这个规律就值得抽成通用表格。视觉相似但数据不同,先放过;数据相似但视觉不同,反而可以抽,样式交给外部控制。
2.2 找“状态逻辑”的重复
AI生成的代码里,除了DOM重复,还有一类隐蔽的重复——状态逻辑。比如每个AI生成的弹窗组件,都会自带一套open/close逻辑;每个表单页面,都有一套validate/reset逻辑;每个列表页,都有loading/error/empty三态逻辑。这些状态逻辑重复,比DOM重复更具抽离价值。
我有一次让AI生成了一整套后台管理页面,总共12个页面,其中8个页面都带有“列表加载失败重试”的逻辑。AI在每个页面里都写了一段几乎一样的fetchList和error.message展示。视觉上每个页面长得不一样,但状态流转一模一样。我当时做了个useListQuery的hook,把这8个页面的请求逻辑统一收口,瞬间删除几百行重复代码。
这里的关键点是:DOM层可以重合度不高,但逻辑层的重合度一旦超过三处,就必须抽。因为逻辑是业务里最容易变的部分,请求参数、错误处理、重试机制,任何一处改动都会引发连锁。抽成hook或混入后,改动只在一个文件里发生。
2.3 识别“结构性位置”的固定搭配
AI生成的代码里,有大量“固定搭配”的结构性位置,比如弹窗里总是“标题+关闭按钮+内容区+底部操作区”,详情页总是“面包屑+标题+描述+分割线”。这些位置关系在AI看来是独立的,但在业务里它们是固定的页面骨架。
这个信号的价值在于:抽出来的组件不一定是一个完整视觉块,也可以是一段布局骨架。我管这叫“半成品组件”,比如ModalFrame、DetailLayout、SearchHeader。AI生成代码时会在每个页面里重复拼这些骨架,但人类前端在写的时候,会直接把它当成Layout复用。
注意:不要一上来就抽“大组件”。从AI代码里抽组件,稳妥的路径是“骨架→区块→单元”。先把布局骨架定了,再逐步把区块填进去,最后才是最小的按钮、输入框。一上来就搞大而全的组件,后续会被需求变化打脸。
3. 我从AI代码里实际抽出的三类复用组件
根据上面的识别信号,我把自己经手过的AI生成代码梳理了一遍,发现最后真正沉淀下来、值得长期维护的复用组件,其实就三类:数据展示类、交互行为类、表单控制类。下面逐一拆开讲。
3.1 数据展示类:信息卡片与列表项
这类组件是最容易从AI代码里抽出来的,因为AI生成列表、卡片时,结构重复率极其高。以React为例,AI生成的典型代码如下:
<div className="user-card"> <img src={user.avatar} alt="" /> <div className="info"> <h3>{user.name}</h3> <p>{user.position}</p> <span>{user.status}</span> </div> </div>如果你让AI在另一个页面生成“订单卡片”,你会发现结构几乎一样,只是字段从user.xxx换成了order.xxx。这种时候,最稳的重构方式不是抽一个Card万能组件,而是抽一个结构容器:
function DataCard({ data, fields, renderAvatar }) { return ( <div className="data-card"> {renderAvatar ? renderAvatar(data) : null} <div className="info"> {fields.map(f => ( <div key={f.key}> <span className="label">{f.label}</span> <span className="value">{f.render ? f.render(data[f.key]) : data[f.key]}</span> </div> ))} </div> </div> ); }把字段配置交给调用方,组件只负责根据配置渲染结构。这样AI生成的新页面,只需要替换fields数组,不用再生成一堆重复的DOM。
我说下为什么不用children硬塞:AI生成的代码里,卡片内部结构并不总是完全一致,有的带操作按钮、有的带标签、有的还有缩略图。把整个内容做成children确实灵活,但会让组件失去约束力,越写越散。折中方案是用“字段配置+插槽”双轨制:共性字段走配置,特殊内容走插槽。
3.2 交互行为类:弹窗/抽屉/折叠面板的控制逻辑
AI生成的弹窗代码,我见过最典型的重复是这样的:
const [visible, setVisible] = useState(false); const openModal = () => setVisible(true); const closeModal = () => setVisible(false);每个弹窗都来一套,且没有统一的关闭行为。比如点击遮罩关闭、按Esc关闭、关闭后重置表单,这些逻辑散落各页面。抽出来不应只抽代码,而是抽一个“交互行为包”。
Vue里可以这样收口:
// composables/useModal.js export function useModal(options = {}) { const visible = ref(false); const open = () => { visible.value = true; }; const close = () => { visible.value = false; if (options.onClose) options.onClose(); }; return { visible, open, close }; }然后在组件里直接用:
const { visible, open, close } = useModal({ onClose: resetForm });这样一来,AI生成的弹窗代码里那份“open/close”样板就叫停了。以后AI再生成新弹窗,我可以让它直接调用已有的useModal,而不是重新写一遍状态。
实际操作里还有一个容易忽略的细节:AI生成的代码经常会把“弹窗状态”和“表单数据”耦合在同一个组件里。抽交互行为时,最好连数据加载状态一起规划进去。比如弹窗打开时就要拉取详情,这个“打开前拉数据”的逻辑也属于交互行为,可以封装成一个useModalWithData,避免每次打开弹窗都重复写useEffect。
3.3 表单控制类:校验、重置、提交状态的管理
AI生成表单的能力对比之下其实偏弱,但有个通病:校验规则写死在组件里,提交状态处理粗糙。它经常生成类似这样的代码:
const handleSubmit = () => { if (!form.name) { setError('请输入姓名'); return; } if (!form.phone) { setError('请输入手机号'); return; } // 提交逻辑... };每个表单页面都来一遍类似逻辑,字段一多,代码直接膨胀。这类复用点,最好的落地方式不是写一坨通用表单组件,而是定一个“表单能力层”——既管理字段值,又管理校验状态和提交状态。
// useFormController.js export function useFormController(fields, rules) { const values = reactive({}); const errors = reactive({}); const submitting = ref(false); const validate = () => { const result = {}; fields.forEach((field) => { const rule = rules[field]; const val = values[field]; if (rule && rule.required && !val) { result[field] = rule.message || '请填写必填项'; } }); return result; }; const submit = async (apiFn) => { const errs = validate(); if (Object.keys(errs).length > 0) { Object.assign(errors, errs); return false; } submitting.value = true; try { await apiFn(values); return true; } finally { submitting.value = false; } }; const reset = () => { fields.forEach(f => { values[f] = ''; errors[f] = ''; }); }; return { values, errors, submitting, validate, submit, reset }; }这样AI生成的表单页面只需要声明字段和规则,剩下的都交给这个控制层。之后需求变更也只用改规则数组,不用动逻辑。
顺嘴提一句,表单校验这块,别迷信通用方案。你让AI生成一个表单,它八成会根据你给的需求字段现场编规则,但字段之间的关系(比如“手机号和验证码必须同时填”),它很难理解。抽表单控制层时,规则里要预留“自定义校验函数”的口子,否则后续扩展依然需要手写。
4. 让AI生成的代码自动“降重”的实操路径
识别了三类复用组件,你的下一步问题通常是:怎么在以后的工作流里,让AI从一开始就生成“不那么重复”的代码?这就涉及到“喂给AI的上下文”了。
4.1 先喂组件清单,再让AI生成页面
以前我直接给AI一句“生成一个用户列表页”,它会输出一整套包括表格、弹窗、表单在内的代码。代码体积大,重复率也高,因为它在独立构建页面。
现在我会先维护一个“组件目录”文件,把已经从AI代码里抽出来的组件名、props、用途写清楚,然后在提问时直接带上:
项目中已有组件:DataCard(props: data, fields, renderAvatar) 已有Hook:useModal(options.onClose)、useFormController(fields, rules) 请使用以上组件,生成一个用户管理页:列表使用DataCard,新增弹窗使用useModal + useFormController。这么做的收益立竿见影:AI生成的代码体量小了一半,重复率大幅下降,而且生成的接口天然符合它们,后续直接放进业务模块就能跑。
4.2 用“反例提示”避免同类重复
AI有个特点:你给它一个好例子,它能学;但你给它一个“不要那么写”的提示,它也真的很吃这一套。我之前重构时,会在项目根目录放一个AI_GUIDELINE.md,里面写清楚哪些写法是禁止的,包括:
- 不要在每页重复写
open/close弹窗状态,使用useModal - 不要为每个列表单独写
loading三态逻辑,使用useListQuery - 不要在组件外面堆砌一堆
if/else校验,把规则放进useFormController
实测下来,只要把这份文件在每次AI对话开头放进去,生成的代码会明显更克制。不是AI变聪明了,而是你给了它更明确的约束边界,它就不敢乱发挥。
4.3 建立自己的代码检索与回填循环
还有一点很多人忽略:AI生成代码的次数越多,你积累的“规律文档”就越有价值。每当我从AI代码里抽出一个新组件,我都会顺手记录两样东西——为什么要抽它和它在哪些AI生成场景里高频出现。
这个习惯看起来麻烦,但长期价值极高。比如过了一周我又让AI生成相似页面,发现它又开始写重复代码时,我就能拿着之前的记录,明确告诉它:“这个模块已经有组件了,请先查看组件目录,不要重新生成。”这种互动循环会让AI越用越顺,因为你的“经验”变成了它上下文里的“规则”。
提示:把组件复用规律写成文字,不只是写给AI看,也是写给未来的自己。两个月后你再看AI生成的代码,早忘了当初为什么抽这个组件,有文档就不会犹豫,该复用就直接用。
5. AI代码复用路上的五个坑
这部分是我踩过坑后的个人总结。AI代码本身没有“复用”的概念,你在它生成的代码里做复用,相当于一份“二次加工”,有些坑绕开了能省很多事。
5.1 复用组件时把样式写死
从AI生成的代码里抽组件,最容易踩的坑是:把AI自带的样式类原封不动带进组件里。AI生成的代码有时会生成一长串不明所以的样式名,抽成组件后你发现样式不符合其它页面的主题,于是要么继承了这个脏样式,要么花大量时间覆盖它。
我的对策是:抽组件时只保留“结构与行为”,把样式类几乎全部剥掉,由使用方传入className或提供主题变量。这不是说组件不能带样式,而是要分清哪些是“这个组件本身的骨架样式”(比如卡片圆角、内边距),哪些是“业务侧定制的表现样式”(比如标签颜色、按钮尺寸)。骨架样式保留,表现样式全部外置。
5.2 抽得太细导致组件爆炸
从AI代码里抽组件,一不留神就会抽“过头”。AI生成的代码里,小到一段加粗文字、一个图标、一条分割线,都可能重复两三次,有些人看到一个重复就抽一个,最后组件库里几十个只有几个字不一样的“字面量组件”。
我的判断标准是:同一段代码在三个及以上场景复用,才考虑抽,而且抽的时候一个组件至少要容纳一个“可变维度”。如果组件props只有文字内容一个维度,那它就是一个文本标签,不要为了抽而抽。
5.3 复用组件成了修改禁区
还有一种情况:组件抽象得太边界分明,结果需求一变,大家都不敢改组件,因为担心影响所有调用方。AI生成代码带来的问题往往是:需求变更是常态,代码原样生成得越多,你的组件越容易成为“不能碰的瓷器”。
我现在的应对是:给复用组件加“扩展白名单”——当新需求发现组件只能覆盖80%的场景时,不是说去改原组件,而是在调用方覆写一个更具体的扩展组件,继承原组件的props,往里加一个功能。合并不是目标,能支撑持续变化才是。
5.4 忽略AI生成代码的无用依赖
AI生成代码时会自动揣测依赖,它可能在没有用到的地方导入useEffect,也可能自作主张引入了某个工具函数。把这些代码抽进组件时,如果你不小心把无用依赖也带进来了,会给组件库带来不少隐含的包体成本。
检查方法不太复杂,抽完组件以后,把AI生成代码里“看起来没用”的导入全部删掉,运行一次编译看报错。这个方法很糙但有效,我的经验是大约有20%的无用依赖会在这一步暴雷。
5.5 只抽组件不抽数据流
最后一个坑,也是最隐蔽的:你从AI代码里抽了一堆展示组件,但数据请求、状态管理、缓存逻辑还是散落在每个页面里。组件复用做得好不好,不能只看视觉层,更要看数据流的复用。
如果每一页都各自负责接口调用和数据状态,那你抽的组件只是替身,真正的业务逻辑还是散装的。我更加倾向于把“取数逻辑”也抽象成Hook层,让页面只组装展示。比如用户列表页调用useUserList,订单页调用useOrderList,它们内部虽然字段不同,但都走同一套加载/缓存/错误处理模板。
数据流不统一时,组件复用只是表面整洁。一旦数据层变了,页面照样要逐个改。这条是我反思最深的。
6. 实操体会:把规律沉淀成团队的共同资产
说一句可能有点反共识的话:AI生成代码的前端项目,比纯手写项目更依赖组件复用,因为AI的产出量非常大,如果不做约束,项目会在半个月内膨胀到失控。我经历过一次:引入AI辅助后,代码量翻了三倍,可维护性直接掉了一半,原因就是没有提前确定复用规律。
后来我把上面这些规律(识别信号、抽取顺序、组件清单、反例提示)全部整理成一份团队内部的“AI辅助前端开发手册”,放在项目仓库的根目录。前端团队成员在开始用AI生成页面之前,先手动阅读一遍这份手册,里面也附了每个已经抽好的组件案例。
实际效果是什么?我从AI拿到的初版代码,跟最终落地的代码之间的差距越来越小。刚开始可能要花两三个小时重构AI输出,现在基本三十分钟内就搞定了。团队新人也更容易上手,因为他们不用从头理解那堆重复代码,直接按照已有组件的接口来调就行。
如果说标题里“找到了规律”这几个字有什么具体的指向,我的答案就是:AI代码里的规律不是AI给的,是你定完规则之后才显现的。先定好哪些结构该抽、哪些状态该收口、哪些接口该暴露,AI的输出质量会被动提高一大截。
这个内容后续我自己还会继续扩展。AI生成前端代码的工具会越来越强,但组件复用的核心思路不会变:你得先知道什么是稳定的骨架,什么是要变的零件,然后把“变的”交给调用方,把“不变的”留在组件里。只要这条边界画得清晰,无论AI怎么生成,你的代码都能兜得住。