v3中reactive中的小坑
“现在我们能造什么?能造桌子椅子,能造茶碗茶壶,能种粮食,还能磨成面粉,还能造纸,但是,一辆汽车、一架飞机、一辆坦克、一辆拖拉机都不能造。”
前两天在写一个比较痛苦的表单,大概是这种: 抽屉内的表单在点击不同radio时会显示不同的待选项,抽屉关闭时所有项要回到初始态。保存时要分别校验不同项下的内容,基础校验是不能为空。
也同之前一样在使用vue3的ref
小坑:关于reactive
const formData = ref({ name: ``, type: 0, // files: [], 'base-text-diff': { baseFileOnly: [] }, 'model-text-diff': { modelFileOnly: [], //单 diffedFileList: [] }, 'appoint-text-diff': { specialList: [], //特定页文件 beforePointFileOnly: [], //特定页,指定前 afterPointFileOnly: [] //特定页,指定后 }, pointPageType: `specialPage`, // 特定页 diffPageType: [], // 比对页:目录封面印章 // 范文页码 specialPage1_start: 1, specialPage1_end: 1, // 比对页码 specialPage2_start: 1, specialPage2_end: 1 })
由于表单太过复杂,且判断较多,于是摒弃了使用ui的表单校验方式,改为手动重置:
// 当修改任务类型后,重置文件列表 watch( () => formData.value.type, (n, o) => { resetTaskType(n, o) resetFileList() } ) const resetTaskType = (newval, oldval) => { // console.log('oldval :>> ', oldval) if (oldval === 2) { formData.value.pointPageType = `specialPage` formData.value.diffPageType = [] formData.value.specialPage1_start = 1 formData.value.specialPage1_end = 1 formData.value.specialPage2_start = 1 formData.value.specialPage2_end = 1 } resetFileList() } // 重置所有绑定的文件数据 const resetFileList = () => { formData.value.['base-text-diff'].baseFileOnly = [] formData.value.['model-text-diff'].modelFileOnly = [] formData.value.['model-text-diff'].diffedFileList = [] formData.value.['appoint-text-diff'].specialList = [] formData.value.['appoint-text-diff'].beforePointFileOnly = [] formData.value.['appoint-text-diff'].afterPointFileOnly = [] }
原本以为这样就ok了,毕竟虽然麻烦但很简单。但是发现它报错了,没有截图,总之就是报了个奇怪的错误
后来就尝试使用reactive声明变量。
第一次有机会使用,说不激动是不可能的,但是在我看来也就仅仅是将value
去掉而已,但是在重置此变量时告警:reactive声明的是常量,无法更改
欺天啦!我声明的是const不假 但是reactive不是用来声明响应式变量的吗??
退一步越想越气,于是翻了翻官方文档:
那该怎么做?
后来翻了翻解决方案,发现一般都是使用此方案:
// 本质上是遍历初始数据后将初始数据重新赋予reactive数据 声明初始化数据: const baseForm = () => ({ name: ``, type: 0, // files: [], 'base-text-diff': { baseFileOnly: [] }, 'model-text-diff': { modelFileOnly: [], //单 diffedFileList: [] }, 'appoint-text-diff': { specialList: [], //特定页文件 beforePointFileOnly: [], //特定页,指定前 afterPointFileOnly: [] //特定页,指定后 }, pointPageType: `specialPage`, // 特定页 diffPageType: [], // 比对页:目录封面印章 // 范文页码 specialPage1_start: 1, specialPage1_end: 1, // 比对页码 specialPage2_start: 1, specialPage2_end: 1 }) //声明reactive变量: const formData = reactive(baseForm()) // 重置reactive变量 const resetFormData = () => { Object.keys(baseForm()).forEach((key) => { formData[key] = baseForm()[key] }) }
如何校验非表单数据?
上面说摒弃了校验表单,因而要自己实现校验功能,由于不同类型要有不同的校验数据,因而有了以下方式 :
// 点击抽屉的确认按钮 const confirm = () => { const checkError = valiteForm() } // 校验 const valiteForm = () => { if (!formData.name) { return { msg: `请输入用户名称` } } switch (formData.type) { // 文本 对比 case 0: if (!formData['base-text-diff'].baseFileOnly.length) { return { msg: `未上传文本比对类型文件,请确认` } } break case 1: if ( !formData['model-text-diff'].modelFileOnly.length || !formData['model-text-diff'].diffedFileList.length ) { const msgType = !formData['model-text-diff'].modelFileOnly.length ? `范文` : `比对` return { msg: `未上传范本比对类型的${msgType}文件,请确认` } } break // 指定页 case 2: if (formData.pointPageType === `specialPage`) { if (!formData.diffPageType.length) { return { msg: `未选择特定页比对中的 比对页,请确认` } } if (!formData['appoint-text-diff'].specialList.length) { return { msg: `未上传特定页比对中的比对文件,请确认` } } } if (formData.pointPageType === 'pointPageNum') { if (formData.specialPage1_end < formData.specialPage1_start) { return { msg: `特定页比对中的范文页页码有误,请确认` } } if (formData.specialPage2_end < formData.specialPage2_start) { return { msg: `特定页比对中的比对页页码有误,请确认` } } if ( !formData[`appoint-text-diff`].beforePointFileOnly.length || !formData[`appoint-text-diff`].afterPointFileOnly.length ) { const msgType = !formData[`appoint-text-diff`].beforePointFileOnly.length ? '范文' : '比对' return { msg: `未上传特定页比对中的${msgType}文件,请确认` } } } break default: break } }
//校验未通过时,会返回一个object,否则返回undefind,因而:
(//校验的方式多种多样,只需让用户感知有误即可,我们甚至可以模仿表单项下手动加一个警示语句如
)
const confirm = () => { const checkError = valiteForm() if (checkError) { return MessagePlugin.warning(checkError.msg) } }
原本写到这里,只需将formdata
数据返回给父组件然后再调用重置方法即可,然而还是有坑:
reactive异步问题
查看以下代码:
const confirm = () => { const checkError = valiteForm() if (checkError) { return MessagePlugin.warning(checkError.msg) } emit('success', formData) emit('update:visible', false) resetFormData() }
逻辑是发送父组件当前数据,随后关闭抽屉,随后重置表单,但是这样写是有误的
,表现是发给父级的是空数据,这是由于重置方法是同步的,而emit则是异步的,原以为只会出现在reacthook
中的问题而今出现在了vue中,因而又有了以下解决方案:
// 实测此方式可大幅减少包体积 import cloneDeep from 'lodash/cloneDeep' const confirm = () => { const checkError = valiteForm() if (checkError) { return MessagePlugin.warning(checkError.msg) } // 预先拷贝一份,同步 const formDataCopy = cloneDeep(formData) //此时formDataCopy为用户填入数据 emit('success', formDataCopy) emit('update:visible', false) resetFormData() }
至此坑就填完了,不过reactive在官方文档中已明确提示不建议继续使用,不明白的是为什么不可以使用ref.value[property]
方式赋值,埋坑
以上。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南