前言
在一次结合v-if/v-else
与resetFields
结合使用reset
之后值不正常,看完源码提了issue
,然后被告知答案原来就在vue
文档里,打脸打脸,因此记录一次不看文档,并且出问题不第一时间找文档的教训。
结论
- 凡事先从文档下手,用
element-ui
不是光看element-ui
的文档,还要看vue
文档 -
Form
组件resetFields
跟data
值没什么关系,只跟Form-Item
创建时的初始值有关。 - 使用
v-if/v-else
时,要先确定组件是需要重新生成(),还是可以直接替换(新组件会使用旧组件的初始值,事实上这里新旧是同一个组件更改了属性)。 - 只有含有
prop
属性,并且当前存在的组件才会reset
。 - 如果
Form
组件不是一开始就创建,可以用nextTick
等待Form/Form-Item
第一次创建完再resetFields
。(这个其实跟组件没多大关系)
事故现场
上面代码切换组件后,执行resetFields
重置值,结果重置的值为别的组件
绑定的初始值。
事故原因
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
也就是说使用v-if/v-else
时,虽然看似是多个组件切换,但实际上是1个组件切换了自身的属性。但问题的关键是,切换了vue
的属性,element-ui
添加的属性比如初始值并没有换。
解决
Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可。
在事故现场上加上key
值绑定。
resetFields过程
form.vue
created() { this.$on('el.form.addField', (field) => { if (field) { this.fields.push(field); } } this.$on('el.form.removeField', (field) => { if (field.prop) { this.fields.splice(this.fields.indexOf(field), 1); } });}resetFields() { ... this.fields.forEach(field => { field.resetField(); }); },
form-item.vue
mounted() { if (this.prop) { this.dispatch('ElForm', 'el.form.addField', [this]); ... let initialValue = this.fieldValue; ... Object.defineProperty(this, 'initialValue', { value: initialValue }); ... } } beforeDestroy() { this.dispatch('ElForm', 'el.form.removeField', [this]); } resetField() { ... this.validateDisabled = true; if (Array.isArray(value)) { prop.o[prop.k] = [].concat(this.initialValue); } else { prop.o[prop.k] = this.initialValue; } }
-
Form
在created
阶段创建监听,保存下当前Form-Item
。 -
Form-Item
在mounted
判断是否存在prop
属性,如果有,设置好初始值。 -
Form
组件resetFields
实际上是保存当前注册上来的子组件Form-Item
的resetField
方法。 -
Form-Item
的resetField
方法重新赋值为this.initialValue
。(之前事故原因就是组件属性更新initialValue
值是不会变的)