前言
在一次結合v-if/v-else
與resetFields
結合使用reset
之後值不正常,看完原始碼提了issue
,然後被告知答案原來就在vue
文件裡,打臉打臉,因此記錄一次不看文件,並且出問題不第一時間找文件的教訓。
結論
- 凡事先從文件下手,用
element-ui
不是光看element-ui
的文件,還要看vue
文件 -
Form
元件resetFields
跟data
值沒什麼關係,只跟Form-Item
建立時的初始值有關。 - 使用
v-if/v-else
時,要先確定元件是需要重新生成(用 key 管理可複用的元素),還是可以直接替換(新元件會使用舊元件的初始值,事實上這裡新舊是同一個元件更改了屬性)。 - 只有含有
prop
屬性,並且當前存在的元件才會reset
。 - 如果
Form
元件不是一開始就建立,可以用nextTick
等待Form/Form-Item
第一次建立完再resetFields
。(這個其實跟元件沒多大關係)
事故現場
https://jsfiddle.net/s5ar1un3…
上面程式碼切換元件後,執行resetFields
重置值,結果重置的值為別的元件
繫結的初始值。
事故原因
Vue 會盡可能高效地渲染元素,通常會複用已有元素而不是從頭開始渲染。
也就是說使用v-if/v-else
時,雖然看似是多個元件切換,但實際上是1個元件切換了自身的屬性。但問題的關鍵是,切換了vue
的屬性,element-ui
新增的屬性比如初始值並沒有換。
解決
https://jsfiddle.net/s5ar1un3…
Vue 為你提供了一種方式來表達“這兩個元素是完全獨立的,不要複用它們”。只需新增一個具有唯一值的 key 屬性即可。
在事故現場上加上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
值是不會變的)
參考
https://cn.vuejs.org/v2/guide…