在用vue做活动的时候接触到一个场景:某一个接口返回了一个数组,数组的每一项都是对象,且对象中的属性也有对象。这个接口返回的数据放在了Vuex中,被命名为awardList,具体的形式如下:
awardList: [
{
name: 'name1',
pic: "http://www.baidu.com",
id: 1,
number: 11,
images: {
}
}, {
name: 'name1',
pic: "http://www.baidu.com",
id: 2,
number: 22,
images: {
}
}, {
name: 'name1',
pic: "http://www.baidu.com",
id: 3,
number: 33,
images: {
}
}
]
在路由的首页使用到了这个数据进行渲染,通过computed获取到awardList
awardList({ $store }) {
return $store.state.awardList
}
在页面中使用awardList进行渲染,但是引入了一个新的属性
<div class="award" v-for="award in awardList" :key="award.id" :class="{has-fetch: award.hasFetch}">
<img :src="award.pic" />
</div>
可以看到,在渲染html的时候,使用到了原数组中的对象不存在的属性,这个属性是后面动态计算的。具体的形式如下:
this.awardList.forEach(award => {
if (award.number >= this.condition && !localStorage['hasFetch' + award.id]) {
award.canGetAward = true
} else {
award.canGetAward = false
}
})
这里的判断条件有多个,因此需要在数组的每一项对象都添加一个属性,以辅助渲染html页面。这里有两种操作方式。第一种如上所述,直接在使用到的时候渲染。这种方式会带来一个问题,后面页面没有根据canGetAward这个属性的改变实时响应。使用console.log打印发现,canGetAward这个属性没有get和set函数,自然没有事实响应。
另一个办法,是在store中创建一个新的带有canGetAward属性的awardList,通过mutation进行改变。如下:
mutation:{
changeAwardList(state) {
let awardList = [
{
name: 'name1',
pic: "http://www.baidu.com",
id: 1,
number: 11,
canGetAward: false,
images: {
}
}, {
name: 'name1',
pic: "http://www.baidu.com",
id: 2,
number: 22,
canGetAward: false,
images: {
}
}, {
name: 'name1',
pic: "http://www.baidu.com",
id: 3,
number: 33,
canGetAward: false,
images: {
}
}
]
state.awardList = awardList;
}
}
这种做法会带来另一个问题:由于computed是一个引用,在后续使用forEach方法对原数组进行了改变,因此会有一个warning提示:
state只允许通过mutation进行修改
因此这里要将this.awardList先深拷贝出来,获得一个新的数组,再通过forEach方法改变新的数组,最后通过mutation将新的数组赋值给store中的state。
实测使用JSON.parse(JSON.stringify(this.awardList))、this.awardList.slice()、this.awardList.concat()等方法均不能达到目的,前两者会导致死循环,后面的提示忘了,好像只是浅拷贝。
已经使用了一个另一个方式避免了这个问题, 但是这个问题确实客观存在,因此记录一下。