1. 只有在初始化 Vue实例前声明的属性才具有响应式能力,后来添加的属性不具备。
场景: 子组件是一个创建/编辑公用的配置页面,当在父组件中点击编辑时,需要将已有的配置数据传递到子组件中展示,当直接对子组件 data属性中的 对象变量 进行赋值时,少添加了一个属性,以致子组件中对该属性变更,页面都没有反应。
<!-- 子组件 template -->
<Form ref="configForm" :model="newConfig" :rules="ruleValidate" :label-width="150">
<Row :gutter="5" v-for="(item, index) in newConfig.dataList" :key="'o-' + index">
<iCol span="10">
<FormItem :label="$t('sms.common.dataList')" :prop="'dataList.' + index + '.id'" :rules="validate.id">
<!-- 遍历出来的每个 <select>中的备选项 即 options,都是 allOptions剔除其他 <select>已被选择的项之后剩下的,即可被当前 <select>选择的备选项 -->
<!-- change事件中实时计算每个 <select>的备选项 -->
<Select v-model="item.id" @on-change="changeData">
<Option v-for="o in item.options" :value="o.id" :key="`o-${o.id}`">{{ o.name }}</Option>
</Select>
</FormItem>
</iCol>
</Row>
</Form>
// 子组件 data数据
data () {
return {
newConfig: {
allOptions: [],
// 空数据结构
// 渲染到页面上是一个 <select> 列表,每个 <select> 中的备选项,即 options都是 allOptions剔除其他 <select>已被选择的项之后剩下的可被当前 <select>选择的备选项
dataList: [
{
id: '',
name: '',
options: []
}
]
}
}
}
// 父组件 调用方法
edit(row) {
// 显示子组件
this.sonShowFlag = true;
// 对子组件 data中的变量赋值
this.$nextTick(() => {
this.$refs.son.newConfig = {
// 原先是这样写的,如果有数据就把数据直接赋值给子组件的这个对象变量,如果没有就赋一个空的对象结构,而 options这个属性是自行添加方便实时计算的,row.dataList中并没有这个属性,导致子组件根据已有数据计算出各个 <select>的 options后,页面并没有渲染出 各个 <select>的备选项,下拉框仍然是空的
itemList: row.dataList.length > 0 ? row.dataList : [
{
id: '',
name: '',
options: []
}
],
// 修正后,为 row.dataList中的每一项添加一个空的 options属性
itemList: row.dataList.length > 0 ? row.dataList.map(item => ({...item, options: []})) : [
{
id: '',
name: '',
options: []
}
]
};
})
},
2. Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。如果需要强制替换元素/组件而不是重复使用它,可以为各个元素/组件分配一个唯一的 key。
场景: Form表单中,结构相同的 <FormItem>根据 v-if 条件切换展示,切换后原先条件下的 <FormItem>的校验报错信息仍然显示在页面上。
<template>
<Form ref="configForm" :model="config" :rules="ruleValidate" :label-width="150">
<FormItem label="选择条件" prop="condition">
<Select v-model="config.condition">
<Option v-for="o in conditionOptions" :value="o.id" :key="`o-${o.id}`">{{ o.name }}</Option>
</Select>
</FormItem>
<!-- 如果不加 key的话,条件1的情况下,“选择设备”的 FormItem 报错而不纠正的情况下直接切换成条件2,根据 Vue的渲染原则,这个 FormItem的 dom会被“选择A端设备”所复用,原先的报错信息因为没有被纠正而保留在页面上 -->
<FormItem label="选择设备" prop="deviceIds" v-if="config.condition == 1" key="device">
<CheckboxGroup v-model="config.deviceIds">
<Checkbox v-for="o in deviceList" :label="o.id" :key="`o-${o.id}`">{{ o.name }}</Checkbox>
</CheckboxGroup>
</FormItem>
<FormItem label="选择A端设备" prop="deviceAIds" v-if="config.condition == 2" key="deviceA">
<CheckboxGroup v-model="config.deviceAIds">
<Checkbox v-for="o in deviceAList" :label="o.id" :key="`o-${o.id}`">{{ o.name }}</Checkbox>
</CheckboxGroup>
</FormItem>
<FormItem label="选择B端设备" prop="deviceBIds" v-if="config.condition == 2" key="deviceB">
<CheckboxGroup v-model="config.deviceBIds">
<Checkbox v-for="o in deviceBList" :label="o.id" :key="`o-${o.id}`">{{ o.name }}</Checkbox>
</CheckboxGroup>
</FormItem>
</Form>
</template>
computed: {
ruleValidate() {
return {
condition : [
{ type: 'number', required: true, message: '请选择条件', trigger: 'change' }
],
deviceIds: [
{ type: 'array', required: true, message: '请选择设备', trigger: 'change' }
],
deviceAIds: [
{ type: 'array', required: true, message: '请选择A端设备', trigger: 'change' }
],
deviceBIds: [
{ type: 'array', required: true, message: '请选择B端设备', trigger: 'change' }
]
}
}
}