用trae+skills将form-generator从Vue2升级到Vue3的实战指南
2026/9/9 4:30:36 网站建设 项目流程

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选项删除,函数代替
异步组件() => importdefineAsyncComponent
v-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文件,包含frontmatter(name和description)和正文规则。AI在处理任务时会根据用户的请求自动判断要不要加载这个skill,加载后再按里面的规则执行。

对于form-generator这种项目,我会给它写两份skill:

  • vue3-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. 构建配置适配 Vite,require 改为 import.meta.env 或静态导入。 ## 输出要求 - 修改代码时必须附带修改说明,格式:文件路径 + 修改点 + 原因。 - 不修改非必要逻辑,保持原有业务行为不变。 - 遇到不确定的兼容点时,列出疑问,不要擅自猜测。

这个模板的核心在于第10条"输出要求"——每处修改都写清楚原因,这样我review的时候效率极高,能直接在改动列表里看到AI的思考过程,而不是自己去diff每一行代码。

2.4 人机协作的节奏怎么安排

有了skill之后,我会上来先告诉trae:"请加载vue3-upgrade-expert技能,开始处理form-generator项目升级,第一轮先把入口和跨项目全局配置改完。" 它就会自动在.skills目录下找到对应文件,按里面的规则执行。

实际跑下来我感觉,人和AI的配合节奏应该是:人负责拆任务、定边界、做code review;AI负责执行批量替换、查漏、跑常规重构。千万不要把整个项目的升级一次性丢给AI,它很容易在长任务里迷失方向,改着改着就写出自认为合理但不符合项目约束的代码。

每完成一轮任务,我会让trae生成一份"本轮改动清单",我再按模块打开几个典型文件检查,确认没有问题再让它进入下一轮。这种任务切分粒度大概在30到60分钟一轮,体感最舒服。

3. 升级实操:按模块拆解每一步

3.1 工程化迁移:从Vue CLI到Vite

form-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.x,vue-router换4.x,vuex换个思路用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属性,旧项目里常写medium,Element Plus不支持了,要变成default
  • 按钮loading、清空图标、输入框前缀这些细节差异,会导致UI在样式上轻微变化,需要逐个页面过一遍。
  • 图标库完全变了。form-generator物料区每个控件前面有个图标,以前是<i class="el-icon-edit">,现在必须这样:
<el-icon><Edit /></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 :is="item.type" v-bind="item.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.env,Vite不会自动注入,需要define字段配置或者换个判断方式。
  • 样式引入顺序问题,Element Plus的样式如果跟业务样式混在一起,很容易出现样式被覆盖。我通常把Element Plus的全局样式放在入口最先引入,业务样式放后面。

这些编译报错其实都是好事,报错至少能定位。最怕的是启动成功、页面白屏、控制台只有个warning,这种运行期隐性问题最难查。

4.2 响应式丢失重灾区

我升级后遇到一个经典场景:主面板的左侧物料区拖拽某个组件后,右侧属性面板一直不刷新。检查后发现,代码里某个地方把响应式对象直接赋值给了一个普通变量,Vue3的reactive是基于Proxy的,一旦把响应式对象经过解构或者赋值给普通属性,代理关系就断了,后续修改都不再触发视图更新。

解决办法很简单:始终操作ref/reactive对象,解构时要用toRefs,不要直接const { a, b } = reactiveObj。这个坑我在skills规范里写成了强制规则:"禁止对reactive对象直接解构赋值,如需解构请用toRefs"。后面一整轮升级里,trae都会主动避开这种写法。

还有一个常见坑:用ref定义表单对象时,在模板里使用v-model="form.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/off:

import 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解构,用toRefs
el-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就能成为你手里最听话的改编执行者。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询