uni-app动态表单项增删须用数组替换或$set,禁用push/splice;索引赋值非响应式,需$set或map;v-for key必须唯一id;提交前应深拷贝校验,避免响应式副作用。

uni-app 动态表单项增删:直接用响应式数组,别 push/splice 后再赋值
uni-app 的 data 数组不是 Proxy,push、splice 等原生方法不会自动触发视图更新——这是最常踩的坑。必须用 this.$set 或替换整个数组才能响应。
常见错误现象:list.push({}) 后界面没新增输入框;list.splice(i, 1) 删除后 DOM 还在;控制台无报错,但视图“卡住”。
- ✅ 正确增项:
this.list = [...this.list, { id: Date.now(), value: '' }](推荐)或this.$set(this.list, this.list.length, { id: Date.now(), value: '' }) - ✅ 正确删项:
this.list = this.list.filter((_, i) => i !== index)(语义清晰、安全)或this.$delete(this.list, index)(注意:仅支持索引删除,且需确保索引有效) - ⚠️ 避免直接调用
this.list.push()、this.list.pop()、this.list.splice()—— 它们不触发更新
为什么不能用 this.list[index] = newItem?
uni-app(基于 Vue 2)对数组索引赋值是**非响应式**的。即使你写了 this.list[0] = { value: 'new' },视图也不会更新,因为 Vue 2 没有劫持数组索引访问器。
使用场景:编辑某一项内容、回填表单数据、异步更新特定条目时容易误用。
- ✅ 替代方案一(推荐):
this.$set(this.list, index, { ...this.list[index], value: 'new' }) - ✅ 替代方案二:
this.list = this.list.map((item, i) => i === index ? { ...item, value: 'new' } : item) - ⚠️
this.list[index].value = 'new'只有在该对象本身是响应式(即初始就在 data 中定义)时才生效,但字段增删仍不可靠
v-for 渲染动态表单时,key 必须唯一且稳定
用 index 当 key 看似方便,但增删会导致 Vue 复用错误的 input 元素,出现值错位、光标丢失、v-model 绑定混乱等问题。
性能影响:key 不稳定会强制重渲染所有项,而非局部更新。
- ✅ 正确做法:每项初始化时生成唯一
id(如Date.now() + Math.random()),渲染时用:key="item.id" - ✅ 示例:
<input v-model="item.value" :key="item.id"> - ⚠️ 错误写法:
:key="index"或:key="item.value"(value 可能重复或为空)
表单提交前校验:别遍历 this.list,要深拷贝再处理
用户可能在输入框中留空、粘贴富文本、触发多次 blur/change,直接操作 this.list 可能引发响应式副作用(比如校验逻辑意外触发视图更新)。
兼容性影响:某些平台(如微信小程序)对频繁 setData 有吞吐限制,原地修改数组 + 频繁赋值易卡顿。
- ✅ 提交前先深拷贝:
const payload = JSON.parse(JSON.stringify(this.list))(简单字段适用)或用lodash.cloneDeep(含函数/循环引用时) - ✅ 校验逻辑独立于 data:
payload.forEach(item => { if (!item.value?.trim()) {...} }),避免污染响应式源数据 - ⚠️ 别在
submit里写this.list[0].value = this.list[0].value?.trim()—— 这会立即触发更新,干扰用户当前输入状态
复杂点在于:动态表单往往带嵌套结构(如子项数组)、联动校验(某项变化影响另一项必填)、以及跨端输入行为差异(App/H5/小程序对 input 事件时机处理不同)。这些没法靠一个数组操作兜住,得在 key 设计、数据快照、事件节流上多留一层判断。

















