在一次实践中,关于再次理解Js对象引用类型的问题
事情是这样的,最近工作中我们的管理系统需要提供员工的技能打分和分值统计表格展示的功能,如下:
首先我们来说说这个表格是怎么通过数据渲染出来的?
表头中雷达图和技能列是通过接口获取渲染出来的动态列
以雷达图为例
<el-table-column label="雷达图" :key="11" align="center"> <el-table-column label="知识技能" align="center"> <el-table-column :label="item.abilityName" v-for="(item, index) in radarKnowledgeSkill" :key="index" align="center"> <template v-slot="{row}"> <el-popover v-if="row.scoreType === '打分值'" placement="right" width="100" trigger="click" @after-leave="editScore(row.employeeAccount, item.colPropName, row[item.colPropName], 0)"> <el-input-number v-model="row[item.colPropName]" size="mini" controls-position="right" :precision="1" :step="0.5" :min="0" :max="10"></el-input-number> <span slot="reference" class="table-cell-innerText">{{ row[item.colPropName] || '--' }}</span> </el-popover> <span v-else>{{ row[item.colPropName] || '--' }}</span> </template> </el-table-column> </el-table-column> <el-table-column label="能力素质" align="center"> <el-table-column :label="item.abilityName" v-for="(item, index) in radarAbilityQuality" :key="index" align="center"> <template v-slot="{row}"> <el-popover v-if="row.scoreType === '打分值'" placement="right" width="100" trigger="click" @after-leave="editScore(row.employeeAccount, item.colPropName, row[item.colPropName], 0)"> <el-input-number v-model="row[item.colPropName]" size="mini" controls-position="right" :precision="1" :step="0.5" :min="0" :max="10"></el-input-number> <span slot="reference" class="table-cell-innerText">{{ row[item.colPropName] || '--' }}</span> </el-popover> <span v-else>{{ row[item.colPropName] || '--' }}</span> </template> </el-table-column> </el-table-column> </el-table-column>
动态列最主要的是根据列名和表格中行数据的id值来拼接出一个有规则的字符串作为它的Prop值,这里我给动态列的数组增加了一个属性colPropName,它的值以“固定字符串+id+类型+类型名”的形式拼接而来
colPropName: 'colRadar_' + el.id + '_' + el.abilityType + '_' + el.abilityName
然后我们需要对表格中的数据进行处理,接口返回的雷达技能数据是下面的这样的一个对象,包含知识技能和能力素质属性,分别对应了包含所有的项一个数组,而这个数组中正好有拼接以上colPropName 值的所有项,我们循环遍历所有项,将以该值作为属性添加到行数据中。这样 Table 组件就可以通过prop值来找到对应关系渲染到表格中。
item.radarMap.abilityQuality.forEach(el => { item['colRadar_' + el.scoreItemId + '_' + el.skillType + '_' + el.skillName] = index % 2 === 0 ? el.referenceValue : el.employeeScore })
接着需要处理的问题是同一个人对应表格中的两行,前两列相同信息合并,我们需要将同一个人的数据复制成两条,然后分别处理参考理论分和打分值。这里关于如何将一个数组项复制成两条,并且相同的数据项临近,抽象出来就是
let a = [ {age: 2, name: 'we'}, {age: 5, name: 'yu'}, {age: 8, name: 'Pl'} ] a = a.map((item) => [item, item]).flat() console.log(a) [ { age: 2, name: 'we' }, { age: 2, name: 'we' }, { age: 5, name: 'yu' }, { age: 5, name: 'yu' }, { age: 8, name: 'Pl' }, { age: 8, name: 'Pl' } ]
这样看着好像没什么问题,同一项被复制成了两份,但是当我对偶数行的age属性值进行加1时,奇数行的数据也变了。是不是很奇怪?于是迅速反应过来,我们在对对象进行复制的时候,其实是复制的引用地址,两份引用地址指向的是同一个值。
a.forEach((el, index) => { if (index % 2 === 0) { el.age += 1 } }) console.log(a) [ { age: 3, name: 'we' }, { age: 3, name: 'we' }, { age: 6, name: 'yu' }, { age: 6, name: 'yu' }, { age: 9, name: 'Pl' }, { age: 9, name: 'Pl' } ]
所以这里复制的时候就应该这样写
a = a.map((item) => [item, {...item}]).flat()
合并简单,表格提供了span-method属性,
mergeSpanMethod ({ row, column, rowIndex, columnIndex }) { if (columnIndex === 0 || columnIndex === 1) { if (rowIndex % 2 === 0) { return [2, 1] } else { return [0, 0] } } }
到这里,这个表格涉及的问题就讲完了。不知道你有没有遇到过类似问题?
看起来很是有点复杂哦。。。