记录一个数组深拷贝的应用场景

772 阅读2分钟

在用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()等方法均不能达到目的,前两者会导致死循环,后面的提示忘了,好像只是浅拷贝。

已经使用了一个另一个方式避免了这个问题, 但是这个问题确实客观存在,因此记录一下。