首先记录两个小点:
1、Array.prototype.find 比 Array.prototype.filter更加的节省性能,因为find是找到需要的数据就直接return,不会继续找了
2、xxx.attribute = xxx.attribute === 1 ? true : false 可以直接简化 =》 xxx.attribute = xxx.attribute === 1
ok,下面进入正题:
之前的一个问题困扰了我很久,这里说一下,我用的是vue2的脚手架,所以antd vue对应的版本是1.x,专门服务于vue2的。
问题是这样的,这个弹窗中的表单是我自己封装的一个组件,这里回显的逻辑是这样的:
<template>
<div class="FormModalComponent">
<a-form :form="form" @submit="handleSubmit">
{{ datas.form_data_set }}
<div v-for="(level1, i1) in datas.form_data" :key="i1">
<a-form-item :label="level1.label">
<!-- 输入框 -->
<div v-if="level1.type == '1'">
<a-input
v-decorator="[
level1.key,
{
rules: [
{
required: level1.required,
message: level1.message,
},
],
},
]"
:placeholder="level1.placeholder"
/>
</div>
<!-- 下拉框 -->
<div v-if="level1.type == '2'">
<a-select
v-decorator="[
level1.key,
{
rules: [
{ required: level1.required, message: level1.message },
],
},
]"
:placeholder="level1.placeholder"
>
<a-select-option
v-for="(level2, i2) in level1.options"
:key="i2"
:value="level2.value"
>
{{ level2.label }}
</a-select-option>
</a-select>
</div>
<!-- 开关 -->
<div v-if="level1.type == '3'">
<a-switch
checked-children="是"
un-checked-children="否"
default-unchecked
v-decorator="[
level1.key,
{ initialValue: false, valuePropName: 'checked' },
]"
/>
</div>
</a-form-item>
</div>
<!-- 确认和取消按钮 -->
<a-form-item :offsetBottom="2">
<a-space>
<a-button type="info" @click="cancelHandleClick">取消</a-button>
<a-button type="primary" htmlType="submit">确认</a-button>
</a-space>
</a-form-item>
</a-form>
</div>
</template>
<script>
export default {
props: {
datas: {
type: Object,
default: () => ({}),
},
},
data() {
return {
form: this.$form.createForm(this, { name: "dynamic_rule" }),
};
},
created() {
console.log(this.datas, "datas");
},
methods: {
handleSubmit(e) {
e.preventDefault();
this.form.validateFields((err, fieldsValue) => {
if (err) {
return;
}
this.$emit("onSubmit", fieldsValue);
});
},
cancelHandleClick() {
this.$emit("onCancel");
},
},
watch: {
datas: {
deep: true,
handler(n, o) {
if (n.visible === true) {
let form_value = {};
n.form_data.forEach((item) => {
for (const key in n.form_data_set) {
if (item.key == key) {
form_value[key] = n.form_data_set[key];
}
}
});
const arr = Object.keys(form_value);
if (arr.includes("reportshow")) {
form_value.reportshow = form_value.reportshow == 1;
}
if (arr.includes("show")) {
form_value.show = form_value.show == 1;
}
if (n.editIndex) {
this.$nextTick(() => {
this.form.setFieldsValue(form_value);
});
}
}
if (!n.editIndex) {
this.form.resetFields();
}
},
immediate: true,
},
},
};
</script>
<style lang="less" scoped>
.FormModalComponent {
height: 11rem;
overflow-y: auto;
}
</style>
如上,我监听了传入进来的数据,通过一系列的数据处理之后,通过this.form.setFieldsValue这个方法进行回显数据,上面是已经完善过的代码,之前一直碰到的一个问题就是,每次初始化,第一次回显总是回显失败,后续全部是成功的。经过分析,问题如下:
问题出处:
问题就出在这个immediate: true上,这个immediate属性,通过打印可以发现,它在created之前就已经执行过了,也就是说,在你的数据通过props传进来并渲染到virtual dom上之前,它就已经把最初始化的数据拿进来渲染过一次了,因此,当你通过调用接口改变数据结构的时候,改变之后的数据,并没有通过这次immediate再次渲染一次,所以第一次失效了,没有回显到表单上。
解决方式:
我的解决方式是通过this.$nextTick,加上一个editIndex是否存在的判断来辅助来解决的,这样可以强制渲染最新的数据,并且保证新增的时候不执行这一条数据。
其他方式:
这里给出几条其他的解决方式:
1、在需要渲染的弹窗上加一个v-if,条件是你需要后续更改的那条数据是否较于之前有变化,比如:一开始form_data是空,赋值后才把数据传入,那么就可以判断v-if = "form_data.length === 0"
另外,watch这个属性,尽量不要监听过大的对象,可以监听这个对象中的多个属性,可以进行优化。