今天遇到一个vue诡异的数据绑定问题,下面我给大家分享一下我解决问题思路和方式,以及为什么要这样。
一、问题现象
使用vant的checkbox组件时无法使用v-model来双向绑定选中状态!但是重新打开checbox所在的pop弹窗,又能显示之前操作的最新的选中值,如果不关闭之后重新打开,始终都不更新,感觉好像没有没有反应一样。
二、问题代码
html代码:
<div v-for="item in siteList" :key="'site-'+ item.id" class="list-site-item" @click="toggleSiteItem(item)">
<div class="site-name">{{ item.siteName }}{{item.siteCode}}</div>
<div class="site-other-info">
<div class="site-check-cont">
<van-checkbox :name="item.id" v-model="item.isChecked" shape="square">
</van-checkbox>
</div>
<div class="site-other-right">
<div class="site-info-item">
<div class="site-info-label">区域:</div>
<div>{{ item.areaName }}</div>
</div>
<div class="site-info-item">
<div class="site-info-label">省份:</div>
<div>{{ item.province }}</div>
</div>
<div class="site-info-item">
<div class="site-info-label">二级单位:</div>
<div>{{ item.companyName }}</div>
</div>
</div>
</div>
<van-divider />
</div>
为了保持接口的kiss,后台接口不返回是否选中“isChecked”这个字段(为了让接口简单,一般接口都不返回后台没有存储的属性)。
原来的js代码:
searchActiveSites({
keywords: that.searchSiteKeywords
}).then(res => {
that.siteList = res.data;
that.siteList.forEach(m => {
m.isChecked = false;
});
that.loadSitefinished = true;
});
动态的加上属性“isChecked”。
切换选中状态:
let that = this;
// that.$refs.checkboxes[index].toggle();
// debugger;
that.siteList.forEach(m => {
if (m.id === item.id) {
m.isChecked = !m.isChecked;
}
});
三、解决问题
searchActiveSites({
keywords: that.searchSiteKeywords
}).then(res => {
that.siteList = res.data;
if (res && res.data.length > 0) {
// Vue.set 等同 this.$set
that.siteList.forEach(m => {
that.$set(m, 'isChecked', false);
});
}
// 诡异问题,不能v-model双向绑定,需要重新打开pop
// that.siteList.forEach(m => {
// m.isChecked = false;
// });
that.loadSitefinished = true;
});
使用$set方法添加对象属性!问题就解决了!
四、问题分析及总结
原来vue官方文档有相关说明:https://cn.vuejs.org/v2/guide/reactivity.html
之前直接赋值添加属性是非响应式的,是动态为根级别的data字段添加property。vue不认为是正常的property属性,也就是没有生成对应setter(你可以console.log看看新加的属性和其他区别),当更改值时也就不能即时通知watcher,正确渲染组件!
我们知道了以上原理之后,使用下面方式也是ok的(亲测有效)
res.data.forEach(m => {
m.isChecked = false;
});
that.siteList = res.data;
注意:
先添加属性,再赋值给data中的属性siteList。反之就不行!!!