
form-generator这个项目老Vue圈的朋友应该不陌生。它是一个拖拽式的表单设计器界面上把输入框、下拉选择、日期选择器这些通用控件往画布上一拖右侧配好字段属性和校验规则点击生成就能拿到一整套可以直接跑的Vue单文件组件代码。对于做后台管理系统、中后台业务的人来说这东西能在表单开发上省一大块重复劳动。我手上一直维护着一个基于Vue2的fork版本最近接了个硬任务整体升级到Vue3。这种老项目升级最磨人的地方在于——看着代码量不大真动起手来全是细节。这次我全程用trae来做配合skills机制把升级流程固化成了一套可执行的规范。这篇文章就把整个升级过程、踩过的坑以及最后沉淀下来的SKILL.md配置和AI协作工作流一次性聊透。1. form-generator升级Vue3先摸清项目底细再动手1.1 form-generator是怎么运转的form-generator是那种典型的重前端逻辑项目核心链路非常清晰左侧组件物料区、中间画布设计区、右侧属性配置区。用户把组件拖到画布后界面产生的是一个JSON格式的schema描述比如组件类型、字段名、校验规则、默认值、布局占比这些信息。真正特别的地方在代码生成功能它根据schema动态拼字符串生成一个完整的Vue文件内容里面包含template、script、style生成出来的代码可以直接嵌进业务项目里使用。所以升级Vue3的时候我面对的不只是一个项目本身的源码还有它生成产物模板代码的语法迁移问题。项目源码要改成Vue3写法生成器拼出来的字符串模板也必须输出Vue3风格的单文件组件不然用户点击生成代码拿到的还是一份老代码这个升级就等于没做完。这一点在动手之前必须先想清楚。从源码结构看它的核心模块大致是这么几个拖拽编排模块负责物料拖入画布、排序、删除、复制这块用到了HTML5拖放和一些排序逻辑。画布渲染模块根据schema动态渲染出真实的表单控件。属性配置面板选中组件后动态展示当前组件的可配置属性。代码生成器把schema序列化成一串Vue文件代码字符串。导入导出/预览把schema存成JSON再通过渲染器还原。这些模块各有各的升级侧重点拖拽模块要解决Vue3兼容、渲染模块要处理动态组件的API变化、生成器模块要重写模板输出。后面我逐块讲。1.2 Vue2到Vue3的真正难点不在语法在习惯先说个结论Vue2到Vue3不是单纯改几行语法的事真正的坑埋在生态迁移和习惯改变里。我列个自己升级时反复用到的对照表维度Vue2习惯Vue3新姿势应用入口new Vue({ render })createApp().mount()全局挂载Vue.prototype.xxxapp.config.globalProperties.xxx响应式声明data()返回对象ref/reactive事件触发this.$emitsetup里用emit事件监听this.$on/$once外部事件总线需引mitt过滤器filters选项删除函数代替异步组件() importdefineAsyncComponentv-modelv-model .sync统一v-model:xxx插槽slot-scopev-slot useSlots组件库Element UIElement Plus构建工具Vue CLI/WebpackVite这些差异光看表没啥感觉真改起来就发现很多旧代码依赖了Vue2的隐形能力比如this上直接拿数据、this.$set补响应式、$children拿子组件、事件总线跨组件通信。这些在Vue3里全变了this.$set消失、$children移除、$on移除改起来不只是换个API而是要把整个数据处理链路重新思考一遍。form-generator因为涉及大量组件间的拖拽联动、画布刷新、属性同步这种隐性依赖特别多最开始直接用自动迁移工具跑了一遍生成的代码一堆编译过不了最后基本是半自动方式完成的。1.3 为什么这次升级要全程用trae加skills这里说下工具选型。trae本质上是个AI优先的代码编辑器里面可以选Claude、GPT这些模型来对话、补全、执行多步任务。最关键的一点是它支持skills机制——在项目里放置.skills目录里面用Markdown文件定义一套技能说明书告诉AI在什么场景下按什么规则执行、用什么样的检查清单、输出什么格式的内容。我这次选择trae加skills是想解决一个核心痛点升级这种老项目AI单次对话是干不完的很容易出现改前一个文件的时候很兴奋改到一半忘了整体约束的情况。skills能把这些约束固化成文档让AI每次动手前都先加载规则这样它就不会把Vue2的语法又写回去也不会把Element UI的用法再用到Element Plus里。这套工作方式本质上就是把老师傅脑子里的经验写下来转成AI的执行规范。后面我会直接给出我给form-generator写的SKILL.md内容并拆解里面每个部分的作用。2. trae与skills把个人经验固化成AI的执行规范2.1 trae的实际操作体验先说trae本身。它用起来跟VS Code差不多界面布局相似快捷键基本通用迁移成本很低。它对项目的理解能力不错打开form-generator这个仓库后它能自动识别这是Vue2加Element UI的项目还会给出一些重构建议。我用的比较多的几个能力对话窗口直接问代码问题比如这段代码里this.$set的作用是什么Vue3应该怎么写它会把定位到的文件和修改点列出来。Builder模式有些版本里叫Agent模式给它一个目标指令它会自己拆解步骤、改多个文件、跑命令最后汇总结果。升级过程中很多重复性劳动比如把data里的对象改成ref、把methods改成普通函数在这个模式下处理得特别快。定位依赖关系它能识别出哪个组件调用了哪个组件升级时不容易漏掉调用方。注意一个小细节trae有自己的agent模式配置如果你同时开了很多能力token消耗会比较快。我这次的做法是在一个会话里专注做一类迁移任务比如本轮只处理全局API迁移不要让它同时干所有事。这个后面会展开。2.2 skills到底是什么怎么理解它skills这名字听起来玄实际上就是一个Markdown说明书。拿我自己做过的比喻你带了个技术还不错的实习生懂Vue、懂JavaScript但他没做过Vue2到Vue3的大规模迁移也不知道form-generator的特殊架构。你得先给他写一份项目迁移注意事项他才会照着规范干活。skills就是给AI的这份迁移注意事项。通常是一个.skills目录里面每个子目录表示一个技能目录内有SKILL.md文件包含frontmattername和description和正文规则。AI在处理任务时会根据用户的请求自动判断要不要加载这个skill加载后再按里面的规则执行。对于form-generator这种项目我会给它写两份skillvue3-upgrade-expert.md通用Vue2到Vue3迁移规范全局API、filters、事件总线这些全写上。form-generator-upgrade.md针对form-generator项目本身的规则比如拖拽排序逻辑不要改数据结构、代码生成器必须输出Vue3模板。把项目特有要求和通用规范分开能提高复用性以后接其他老项目升级时第一份skill可以直接复用。2.3 给form-generator升级写一份SKILL.md实战模板直接上干货这是我实际用过的SKILL.md结构压缩成了最核心的版本--- name: vue3-upgrade-expert description: 将Vue2项目升级到Vue3时使用按本规范检查和修改代码确保输出符合Vue3和Vite生态的代码。 --- # Vue2 到 Vue3 升级规范 ## 触发条件 - 用户要求将Vue2项目/组件迁移至Vue3 - 检测到data/methods/created/Vue.prototype等Vue2写法 ## 必须遵守的规则 1. 入口文件必须使用 createApp禁止 new Vue。 2. 全局属性挂载到 app.config.globalProperties禁止 Vue.prototype。 3. 数据响应式优先级ref 处理基本类型reactive 处理对象失去响应式的赋值必须重新声明。 4. 禁止出现 this.$set、this.$delete、Vue.observable用 ref/reactive 替代。 5. 移除 filters在模板中直接调用函数或使用 computed。 6. 事件总线禁止使用 Vue.prototype.$bus改用 mitt 等独立库。 7. Element UI 组件必须替换为 Element Plus注意 - el-dialog 的 :visible.sync 改为 v-model - size 属性 medium 改为 default - 图标统一使用 element-plus/icons-vue 8. 异步组件必须用 defineAsyncComponent 包裹。 9. 删除所有 .native 修饰符普通事件绑定即可。 10. 构建配置适配 Viterequire 改为 import.meta.env 或静态导入。 ## 输出要求 - 修改代码时必须附带修改说明格式文件路径 修改点 原因。 - 不修改非必要逻辑保持原有业务行为不变。 - 遇到不确定的兼容点时列出疑问不要擅自猜测。这个模板的核心在于第10条输出要求——每处修改都写清楚原因这样我review的时候效率极高能直接在改动列表里看到AI的思考过程而不是自己去diff每一行代码。2.4 人机协作的节奏怎么安排有了skill之后我会上来先告诉trae请加载vue3-upgrade-expert技能开始处理form-generator项目升级第一轮先把入口和跨项目全局配置改完。 它就会自动在.skills目录下找到对应文件按里面的规则执行。实际跑下来我感觉人和AI的配合节奏应该是人负责拆任务、定边界、做code reviewAI负责执行批量替换、查漏、跑常规重构。千万不要把整个项目的升级一次性丢给AI它很容易在长任务里迷失方向改着改着就写出自认为合理但不符合项目约束的代码。每完成一轮任务我会让trae生成一份本轮改动清单我再按模块打开几个典型文件检查确认没有问题再让它进入下一轮。这种任务切分粒度大概在30到60分钟一轮体感最舒服。3. 升级实操按模块拆解每一步3.1 工程化迁移从Vue CLI到Viteform-generator原本是Vue CLI搭建的第一步我建议先把它切换到Vite。这一步不完全是追新而是Vite启动速度快、依赖安装简单、生态已经足够成熟Vue3项目用Vite几乎是标准姿势后面接Element Plus、自动导入插件也顺手。迁移要点新增vite.config.js配置别名指向src目录并加上Element Plus自动导入相关的插件如果你用了unplugin-vue-components的话。把public/index.html移到根目录script标签改成引用/src/main.js。package.json里的依赖换掉vue升级到3.xvue-router换4.xvuex换个思路用pinia也可以暂不引入状态库把原来少量全局状态用reactive简单替代。vite的define属性里如果有process.env的引用需要补齐。这一步是体力活但很容易出幺蛾子。那次trae在自动迁移时把main.js改成这样import { createApp } from vue import ElementPlus from element-plus import element-plus/dist/index.css import App from ./App.vue const app createApp(App) app.use(ElementPlus) app.mount(#app)看着没问题但我原项目里有往Vue.prototype上挂方法的代码比如全局的API请求函数和工具类这些在升级后必须改成globalProperties否则原来组件里写成this.$api的地方全部会挂掉。我给trae的技能规范里明确写了这类替换规则它才会自动把所有this.$api.xxx替换掉同时把挂载方式改干净app.config.globalProperties.$api apiService这个坑特别值得提如果遗漏前端所有接口都会报TypeError: Cannot read properties of undefined而且报错位置分散在每个组件里排查起来很要命。3.2 代码层迁移Options API到Composition API怎么取舍form-generator的组件大多用Options API写成升级路径上有两个选择一是把每个组件原样保留Options API写法只调整Vue3不兼容的API二是重写成Composition API。我建议分情况处理。对核心模块比如画布组件和代码生成器组件值得改成Composition API。因为Vue3的优秀特性ref、reactive、watchEffect会让这一类状态联动强的逻辑更清晰。比如画布上当前选中的组件索引、画布列表、属性面板的联动用ref和computed来写比在data和methods里来回找this要自然很多。对配置面板里的一些纯展示组件保持Options API也没有问题Vue3完全兼容只要能跑、一致性好不用为了重构而重构。一个代表性改动是原来的data() { return { formList: [], activeIndex: -1 } }, computed: { activeForm() { return this.formList[this.activeIndex] || null } }, methods: { addComponent(item) { this.formList.push(item) } }在核心组件里我让trae改成了import { ref, computed } from vue const formList ref([]) const activeIndex ref(-1) const activeForm computed(() { return formList.value[activeIndex.value] || null }) function addComponent(item) { formList.value.push(item) }这里有个非常容易翻车的地方ref包装的数组或对象在模板里会自动解包但在JavaScript逻辑里必须通过.value访问。很多AI改代码时忘了加value或者把一个ref塞进另一个reactive里导致绑定丢失。trae的skills规则里我特意加了一条ref的value访问只能出现在script中模板中解包由编译器处理它就能减少这类低级错误。3.3 组件库迁移Element UI到Element Plus的细节form-generator重度依赖Element UI这一步躲不掉。Element Plus整体风格跟Element UI很像但破坏性改动不少。高频踩坑清单el-dialog的:visible.sync变成了v-model这个form-generator里弹窗用得很多比如预览弹窗、导入JSON弹窗全部要改。el-radio-group、el-checkbox-group的value绑定方式基本兼容但要检查是否用了label作为值Element Plus里label行为有变化建议分离value和label。size属性旧项目里常写mediumElement Plus不支持了要变成default。按钮loading、清空图标、输入框前缀这些细节差异会导致UI在样式上轻微变化需要逐个页面过一遍。图标库完全变了。form-generator物料区每个控件前面有个图标以前是i classel-icon-edit现在必须这样el-iconEdit //el-icon并且要在main.js里注册所有用到的图标import * as ElementPlusIconsVue from element-plus/icons-vue for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) }我让trae处理时直接在skills规范里要求它所有图标必须从element-plus/icons-vue导入禁止使用el-icon-开头的class它就能在整库范围里做替换比人肉找快得多。替换后我跑一遍页面挨个图标确认显示不出来的再补。3.4 核心难点拖拽、渲染器、代码生成器这是form-generator升级最硬的部分也是我在trae上花时间最多的地方。先看拖拽。原项目用了HTML5拖放接口本身不依赖第三方库但画布内的排序和属性传递依赖的事件参数在Vue3里没有太大变化主要是要改掉对this.$refs的依赖习惯。本来我一度想引入vuedraggable后来一想改动太大还是保留原生拖放只把方法里this相关的地方纠正过来。如果项目里用了vuedraggable的话注意要换到4.x版本支持Vue3或者直接用sortablejs裸写。这一点我吃过大亏之前另一个项目里直接用了vuedraggable旧版本拖拽整个失效控制台还看不到报错定位了很久。再看渲染器。表单设计器保存的schema是通过动态组件渲染出来的component :isitem.type v-binditem.props /Vue3的动态组件和Vue2没有本质区别但form-generator里有些组件是异步加载的比如富文本、代码编辑器这类重型组件以前是components: { MyEditor: () import(...) }Vue3必须包一层defineAsyncComponent这个我在skills规范里写了规则之后trae替我把所有异步组件声明一次性修正了。然后是代码生成器这是最容易漏的模块。它本身是一个函数输入schema输出一个JavaScript字符串这个字符串里是完整的Vue2单文件组件代码。升级后这个字符串必须改成Vue3模板包括去掉filters、data改setup写法或保持options写法但去掉Vue2专属API、v-model的语法更新、组件引用从Element UI改成Element Plus、引入方式写成Vite风格。举个例子原来生成的代码可能是export default { data() { return { form: { username: } } }, methods: { submit() { this.$emit(submit, this.form) } } }升级后生成的应该是import { reactive } from vue export default { emits: [submit], setup(props, { emit }) { const form reactive({ username: }) function submit() { emit(submit, form) } return { form, submit } } }这个字符串模板的代码在项目源码里其实是一长串拼接字符串直接让AI处理很容易漏改某一段。我的做法是先让trae从代码生成函数里抽取一段样例输出手动确认符合Vue3规范再让trae对照样板去改剩下的拼接逻辑。这个先定标准输出再改生成器的顺序特别有效。3.5 动态表单设计器独有的业务逻辑不能破坏有一点在升级时特别容易犯迷糊form-generator这种表单设计器内部schema的数据结构是经过精心设计的很多字段名、层级关系跟Element Plus的props并不是一一对应。升级时不能简单地把所有props直接映射到Element Plus组件上因为Element Plus和Element UI的prop名称有差异比如某些组件的校验相关字段。如果盲目照搬保存后的旧模板导入进来会乱掉。我这边采取的做法是保持schema内部结构不变只在渲染器和代码生成器层做适配。也就是把旧模板的数据兼容进去渲染时再转换到新版组件库的props。这个设计让升级风险降低了很多trae在改代码时我也反复强调不要动schema结构它就不会乱动核心对象的数据映射逻辑。4. 升级路上踩过的坑与排查实录4.1 编译期与运行期的坑升级到Vite之后我遇到的第一波问题集中在编译期require is not defined这是因为Vite不认CommonJS的require写法凡是源码里出现require的地方全部要改常见于第三方配置文件的动态引入。process is not defined有些工具库或者旧代码直接在浏览器环境用了process.envVite不会自动注入需要define字段配置或者换个判断方式。样式引入顺序问题Element Plus的样式如果跟业务样式混在一起很容易出现样式被覆盖。我通常把Element Plus的全局样式放在入口最先引入业务样式放后面。这些编译报错其实都是好事报错至少能定位。最怕的是启动成功、页面白屏、控制台只有个warning这种运行期隐性问题最难查。4.2 响应式丢失重灾区我升级后遇到一个经典场景主面板的左侧物料区拖拽某个组件后右侧属性面板一直不刷新。检查后发现代码里某个地方把响应式对象直接赋值给了一个普通变量Vue3的reactive是基于Proxy的一旦把响应式对象经过解构或者赋值给普通属性代理关系就断了后续修改都不再触发视图更新。解决办法很简单始终操作ref/reactive对象解构时要用toRefs不要直接const { a, b } reactiveObj。这个坑我在skills规范里写成了强制规则禁止对reactive对象直接解构赋值如需解构请用toRefs。后面一整轮升级里trae都会主动避开这种写法。还有一个常见坑用ref定义表单对象时在模板里使用v-modelform.username用户输入时页面正常但在setup里手动赋值form.value newData结果视图不更新。因为ref的值整体被替换时如果新值没有被reactive处理就会失效。正确做法是form.value reactive(newData)或者直接把form改成reactive。这类细节Non-AI工具基本不会帮你发现全靠经验和规范卡住。4.3 事件总线替换方案form-generator里存在一些跨组件通信场景比如物料区的点击事件通知画布区加组件属性面板的修改通知画布刷新。Vue2里很多人图省事直接用根实例当事件总线this.$root.$emit(some-event, payload)Vue3中这么做已经行不通$on/$emit从根实例上移除了。我这次的替代方案是引入mitt很轻量几十行代码只用它的emit/on/offimport mitt from mitt export const emitter mitt()原来的this.$root.$emit(xxx)改成emitter.emit(xxx)this.$root.$on(xxx)改成emitter.on(xxx)。注意组件卸载时记得emitter.off清理监听避免内存泄漏和重复触发。用trae做这种全局替换时我会在skill里明确所有通过$root、$parent触发的事件都要改为引入mitt它就能帮我找齐所有散落的事件调用。4.4 生成模板字符串的兼容性问题代码生成器输出的字符串模板是form-generator升级里最特殊的一个点。它不会在编辑器里报错只有用户把生成的代码拿出去运行后才会暴露问题。我做完主项目升级后专门生成了一段包含输入框、下拉框、日期选择器、按钮的表单代码放到一个新开的Vue3项目里跑结果还是发现了几个问题生成的script里还残留着this.form.xxx的写法Vue3的setup里this不是组件实例。el-form的:model绑定用到了this模板里应该是直接引用变量名。生成的代码里还带了一个Vue2时代才有的filters虽然Vue3里定义filters不会有编译报错但模板里用了| filterName会直接失效。解决思路就是前面说的先把一段样例输出手动打磨成标准产物再让trae按这个标准去更新生成器的拼接逻辑。我在skill里加了一条规则代码生成器的输出必须以examples/example-vue3.vue为验收基准输出后对比差异。 以后它再改生成器就会先对照基准文件。4.5 常见问题速查表我把这次升级里出现频率最高的问题整理成一张表方便遇到同类情况时快速定位现象可能原因解决思路启动即报require is not definedCommonJS写法未改改用ESM import或import.meta所有接口都undefinedVue.prototype挂的全局属性没换改globalProperties调用处this.$api统一处理表单输入不触发校验ref/reactive绑定丢失检查是否对reactive解构用toRefsel-dialog打不开:visible.sync未改改成v-model图标全变问号还在用旧的el-icon class用element-plus/icons-vue注册组件事件多次触发mitt监听未清理onUnmounted里off拖拽失效vuedraggable旧版换sortablejs或新版适配生成的代码跑不起来生成器字符串模板还是Vue2对照基准样例重写生成器5. 用trae加skills做升级的实战心得与复盘5.1 SKILL.md的迭代才是核心资产整个项目升级下来我最深的感受是真正有价值的不只是跑通的代码而是那几份不断迭代的SKILL.md文件。它把这次升级中踩过的坑、总结出的规则、针对form-generator的特殊约束全部固化下来了。下次再遇到Vue2项目升级我不用从零开始踩坑直接把skill丢给AI它就能按照这些规范完成大部分机械性工作。我在升级过程中养成了一个习惯每遇到一个新问题解决后立即往skill里追加一条规则。比如日期组件格式化参数在新旧版里有差异弹窗关闭后表单校验需要resetFields这些一开始skill只有十来条到最后已经扩展成了一份非常实用的升级避坑手册。5.2 哪些活适合交给AI哪些必须自己把握从效率角度看这类AI工具最适合处理的是大范围、高重复、规则明确的任务。比如全局搜索this.$set并改写、统一替换图标引用、把el-dialog的属性挨个改成v-model这些工作量大且不需要过多判断AI做得又快又全。但涉及架构决策的事我建议还是人自己拿主意。比如schema结构改不改、组件间通信方案用不用mitt、渲染器要不要整体重构这些牵一发动全身AI没有业务大局观让它决策风险很高。你可以让AI给出方案对比但最终拍板我来。另外AI在长上下文里容易遗忘早期给它设定的约束所以任务切分尽量小每个任务结束时让它执行一遍检查清单。我在一个任务里同时让它改图标、改弹窗、改事件总线结果它处理完图标后回到弹窗时图标注册逻辑差点又被覆盖。后来我的做法是一个任务只做一类改动完成并验证后再开下一个安全性高很多。5.3 升级完成后的扩展方向form-generator升级到Vue3之后整体架构清爽了可以做的事也变多了。Element Plus的原生主题定制能力更强下一步可以做个可视化配置主题的功能Vite的插件生态让按需加载更简单物料区组件可以做成按需异步加载生成器的输出模板也可以增加更多选项比如生成TS版本、生成带接口请求的版本。我个人最想做的扩展是把schema数据结构再规范化一下让它能对接更多的后端低代码平台。因为表单设计器最值钱的就是那份描述表单的schema只要它稳定前端渲染器、代码生成器、甚至以后接移动端组件库都只是做适配层的问题。这次升级工程前后花了差不多一周时间其中一半时间花在踩坑和沉淀规则上。trae加skills这套组合让我确信未来这类老项目重构不再需要纯靠人肉去翻遍每一个文件只要经验能写成规范AI就能成为你手里最听话的改编执行者。