为何未修改Vuex值却出现自动更新?
我搞不懂为什么只修改组件自己的data()变量时,Vue devtools显示我的Vuex数据也发生了变化。
我用Vuex存储获取到的chartData,用来填充ChartJS柱状图。这份数据通过chartDividendsProps这个props传递给DividendChart组件,之后我把chartData加载到ChartJS柱状图组件中。现在点击触发timeFrameChange(e)的按钮时,Vuex中的数据莫名其妙被修改了。
当用户点击“year to date”按钮时触发timeFrameChange方法,该方法会调用getDividends和setYearToDate:
timeFrameChange(e) { this.isLoading = true; setTimeout(() => { this.selectedTimeFrame = e.target.innerText; this.isThereData ? this.getDividends() : null; this.setYearToDate(); this.isLoading = false; }, 1); }
从props中获取数据并加载到this.chartData的getDividends方法:
getDividends() { this.dividendsArray = []; this.chartData.datasets = []; this.dividendsArray = this.chartDividendsProps // PROPS this.chartData.labels = this.dividendsArray[0].datesList; // fill stacked chart for (let i = 0; i < this.dividendsArray.length; i++) { this.chartData.datasets.push({ label: this.dividendsArray[i].name, isin: this.dividendsArray[i].isin, backgroundColor: "#007cda", borderRadius: 4, borderWidth: 0, borderSkipped: "bottom", borderColor: "transparent", borderDash: [10, 5], barPercentage: "1", hoverBorderColor: "transparent", data: this.dividendsArray[i].dividendsList, }); } }
用于修改图表时间范围的setYearToDate方法:
setYearToDate() { // delete all months before this year let currentYear = new Date().getFullYear(); for (let i = 0; i < this.dividendsArray.length; i++) { for (let j = 0; j < this.dividendsArray[i].dividend.length; j++) { let dividendYear = parseFloat(this.dividendsArray[i].dividend[j].date.split("-")[1]); if (dividendYear < currentYear) { this.dividendsArray[i].dividend.splice(j, 1); this.dividendsArray[i].datesList.splice(j, 1); this.dividendsArray[i].dividendsList.splice(j, 1); j--; } } } },
原因分析
核心问题是引用类型的浅拷贝:Vuex中存储的chartData是数组/对象这类引用类型数据,当你执行this.dividendsArray = this.chartDividendsProps时,并没有复制一份全新的数据,只是让dividendsArray指向了Vuex数据所在的内存地址。之后在setYearToDate里用splice修改dividendsArray的内容,本质上是直接修改了内存中的原数据,也就是Vuex里存储的那份数据。
解决方案
方法1:使用JSON序列化实现深拷贝,修改
getDividends中的赋值语句:this.dividendsArray = JSON.parse(JSON.stringify(this.chartDividendsProps));这种方式简单易用,但不支持函数、Symbol等特殊数据类型。
方法2:使用递归实现自定义深拷贝函数,或者借助第三方工具(比如Lodash的
_.cloneDeep)处理复杂数据结构。方法3:在
getDividends中只提取需要的字段并创建新对象/数组,避免直接引用原数据,比如:this.dividendsArray = this.chartDividendsProps.map(item => ({ ...item, dividend: [...item.dividend], datesList: [...item.datesList], dividendsList: [...item.dividendsList] }));
内容的提问来源于stack exchange,提问作者David

