最近在写 Vue3 + Element Plus 的后台系统,遇到了一个贼恶心的问题。平时开发,我们肯定都是复用同一个弹窗来做“新增”和“编辑”的嘛。结果发现一个诡异的 Bug:只要先点了“编辑”,关掉弹窗再去点“新增”,表单里的数据不仅没清空,控制台还疯狂报 Cannot read properties of null 的红错。
搞了半天,终于把这个连环坑给盘明白了,必须记录一下防坑!
💥 翻车现场
先看看我一开始是怎么写的,直觉上完全没毛病:
// 新增按钮
const handleAdd = () => {
dialogState.visible = true
// 想着弹窗开了,顺手清空一下表单
formRef.value.resetFields()
}
// 编辑按钮
const handleEdit = (row) => {
dialogState.visible = true
// 弹窗开了,把当前行的数据塞进去
Object.assign(formModel, row)
}
结果就是,点新增直接报错崩溃,数据还死死拽着上一次编辑的值不放。
🕵️♂️ 到底发生了啥?(连环车祸)
排查后发现,这根本不是一个 Bug,而是两个机制撞在一起导致的“连环车祸”:
第一坑:你太急了,DOM 还没出来呢 Vue 更新页面是异步的。你刚让
visible = true,<el-form>其实还没画到网页上。你紧接着去调resetFields(),它去哪找表单实例?当然直接报null的错。而且因为报错了,后面的代码直接罢工中断,清空操作直接无效。第二坑:Element 的“初始值”玄学 哪怕你解决了报错,数据依然清不掉!因为
resetFields()并不是“把表单清空”,而是“恢复到表单第一次挂载时的状态”。 如果你先点了“编辑”,表单第一次出来的时候,里面就已经被你塞满了编辑的数据。Element 就会把这坨数据当成它的“出厂设置”。以后你再怎么调重置,它都只会恢复到这第一次编辑的样子。真的是绝了!
🛠️ 怎么填坑?
搞懂了原因,解决起来就两句话的事儿,核心就是用好 nextTick。
1. 修改新增方法(解决报错): 必须等弹窗老老实实渲染出来了,再去清空。
import { nextTick } from 'vue'
const handleAdd = async () => {
dialogState.visible = true
// 等一等,让弹窗和表单先画出来
await nextTick()
// 稳妥起见加个问号 ?. 再重置,彻底告别 null 报错
formRef.value?.resetFields()
}
2. 修改编辑方法(解决初始值坑): 这是最关键的一步!一定要等表单干干净净地挂载完,让 Element 记住“空状态才是出厂设置”,然后再把数据塞进去。
const handleEdit = async (row) => {
dialogState.visible = true
// 先等表单以空数据的状态渲染完,定格干净的初始值
await nextTick()
// 然后再去赋值,完美避开污染
Object.assign(formModel, row)
}
总结下来就是:新增时,先 nextTick 再重置;编辑时,先 nextTick 再赋值。 以后再遇到这种弹窗复用的场景,直接套这个公式就完事了!