解决 数据与UI更新不同步的坑

40次阅读

共计 3341 个字符,预计需要花费 9 分钟才能阅读完成。

Bug 情景再现
<template>
<div class=”store-box”>
<div v-for=”(day, d) in dayList” class=”checkItem-box”>
<span>{{day}}</span>
<el-checkbox-group v-model=”storeItem.arrange[d]”>
<el-checkbox :label=”1″> 上午 </el-checkbox>
<el-checkbox :label=”2″> 下午 </el-checkbox>
<el-checkbox :label=”3″> 晚上 </el-checkbox>
</el-checkbox-group>
</div>
</div>
</template>

<script>
export default{
data(){
dayList: [‘ 周一 ’, ‘ 周二 ’, ‘ 周三 ’, ‘ 周四 ’, ‘ 周五 ’, ‘ 周六 ’, ‘ 周日 ’],
storeItem: {
arrange: []
},
},

mounted(){
const week = [
[1,2], // 周一的选择情况
[1], // 周二的选择情况
[], // 周三的选择情况
[2,3], // 周四的选择情况
[2,1],
[1,2,3],
[3]
]
// 为 checkbox-group 赋值
this.storeItem.arrange = week;
}
}
</script>
得到这样的结果:
但是当我们点击其它选择框的时候,没有反应。
尝试解决
最先想到的原因应该是 vue 没有对 arrange 的改变监控到,于是解决办法是使用 vue. set
this.$set(this.storeItem, ‘arrange’, week)
这样修改后确实也起作用了。⭐ 注意:这里使用 vue.set 起作用根本是,瞎猫碰上死耗子,<el-checkbox-group> 不能相响应的原因根本不是这个而且我们仔细读 Vue 的官方文档 Vue.set(target, key, value) 作用是向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如 this.myObject.newProperty = ‘hi’)
这里我们根本没有添加 arrange 属性,而只是去改变了 arrange 属性的值,vue 按理说应该是能够检测到的。后面的实验也认证了我的观点。
实验:让我们再把表单复杂一些。注:部分代码已经省略
<template>
<div v-for=”(store, st) in serviceStoreList” class=”store-box”>
<label>{{store.name}}</label>
<div v-for=”(day, d) in dayList” class=”checkItem-box”>
<span>{{day}}</span>
<el-checkbox-group v-model=”store.arrange[d]”>
<el-checkbox :label=”1″> 上午 </el-checkbox>
<el-checkbox :label=”2″> 下午 </el-checkbox>
<el-checkbox :label=”3″> 晚上 </el-checkbox>
</el-checkbox-group>
</div>
</div>
</template>

<script>
export default{
data(){
dayList: [‘ 周一 ’, ‘ 周二 ’, ‘ 周三 ’, ‘ 周四 ’, ‘ 周五 ’, ‘ 周六 ’, ‘ 周日 ’],
serviceStoreList: [
name: ”,
arrange: [],
],
},

mounted(){
const week = [
[1,2], // 周一的选择情况
[1], // 周二的选择情况
[], // 周三的选择情况
[2,3], // 周四的选择情况
[2,1],
[1,2,3],
[3]
]
// 为 checkbox-group 赋值

for (let i=0; i<3; i++){
this.serviceStoreList[i].name = ` 这是 ${i} 号 `;
this.serviceStoreList[i].arrange = week;
}
}
}
</script>
这样写了之后,checkBox 还是:能够展示勾选,但是点击其它选择框没有任何反应。
于是我们将 mounted 里面的代码改成
const week = [
[1,2], // 周一的选择情况
[1], // 周二的选择情况
[], // 周三的选择情况
[2,3], // 周四的选择情况
[2,1],
[1,2,3],
[3]
]
// 为 checkbox-group 赋值

const _obj = {};
const self = this;

for (let i=0; i<3; i++){
_obj = {};
_obj.name = ` 这是 ${i} 号 `;
_obj.arrange = week;
self.$set(self.serviceStoreList, i, _obj)
// 或
self.serviceStoreList.splice(i, 1, _obj)
}
还是不会起任何作用,点击其它选择框依然是点不动,不能进行交互。
这里我反复实验了几次,发现了一个特殊的现象。就是当你去点击其它选择框的时候,vue-devtool 里面显示的数据其实是已经改变了的。而且,当你点击了一个 checkBox,然后去填写另外的一个表单项,比如去选择下拉,或者填写其它 <el-input> 的时候,checkBox 立刻就改变了。相当于它之前的 v -model 的数据其实是正常的,只是视图卡住了。
最后的解决办法
vue 的数据没有问题,那么肯定是 element 埋下的坑。个人认为 <el-checkbox-group> 与 <el-check> 的数据同步有些问题。这样写就好了
<el-checkbox-group v-model=”store.arrange[d]”>
<el-checkbox :label=”1″ :checked=”checked” @change=”checked=!checked”> 上午 </el-checkbox>
<el-checkbox :label=”2″ :checked=”checked” @change=”checked=!checked”> 下午 </el-checkbox>
<el-checkbox :label=”3″ :checked=”checked” @change=”checked=!checked”> 晚上 </el-checkbox>
</el-checkbox-group>

//data 里面增加一个字段
data (){
checked:false , // 这个 checked 没有任何作用,只是为了绕开 elment 的这个坑
}
这样我们不用设置 vue.set(因为 vue 其实重头到尾都能够监视到数据的改变)
const week = [
[1,2], // 周一的选择情况
[1], // 周二的选择情况
[], // 周三的选择情况
[2,3], // 周四的选择情况
[2,1],
[1,2,3],
[3]
]
// 为 checkbox-group 赋值
const _obj = {};
const _store = [];
for (let i=0; i<3; i++){
_obj = {};
_obj.name = ` 这是 ${i} 号 `;
_obj.arrange = week;
_store.push(_obj);
}
this.checked = false; // 需将 checked 设置为 false,不然选择框可能会出现全部选中的情况
this.serviceStoreList = _store; // 直接设置,checkbox 也能正常交互
心得

vue 只是 无法探测普通的新增属性,但是 Vue 能够探测到 data 里面已经注册过的对象的改变,比如重新给这个对象赋值,或改变它已经注册过的属性的值(非给它新增其它属性)。无论这个对象的数据结构有多么的复杂。
少用 vue.set, 多在 <el-check> 上面显示指明 @change,让它状态改变。
再每次修改 <el-checkbox-group> 的 v-model 的值的时候,先将 checked 设置为 false

1. this.checked = false
2. this.serviceStoreList = _store

正文完
 0