背景
有个需求跟之前一个需求类似。项目进度比较赶。正好发现我用技术栈跟之前类似。于是我拷贝过来了。这几天有新的改动点进来,于是我看了看之前的代码。觉得在此基础上改动,还是让我重写吧。
唉,别想省事,早晚都是你的。
好好审视了下之前的代码,吐槽没用。又想了想之前挪用的界面,有些逻辑不太合适。我会跟产品商量下。我还觉得有些php接口似乎设计的也不甚合理。当然每个人想法不同(说这句话真是万全)。
另,写本篇文章,没有任何敌意。只是记录一次重构。
先看看之前的
我觉得变量的设计应该符合最终接口要求的格式。
先看下接口要求的提交参数
......
[
{
"sku_attr":[
{
"attr_id":"3219",
"attr_name":"内存",
"attr_value_id":"370045",
"value_name":"64G"
},
{
"attr_id":"3220",
"attr_name":"颜色",
"attr_value_id":"370042",
"value_name":"绿色"
},
{
"attr_id":"3221",
"attr_name":"尺码",
"attr_value_id":"370039",
"value_name":"L"
}
],
"sku_stock":1,
"sku_price":1,
"sku_org_price":1,
"vip_price":1,
"weight":"333",
},
{
"sku_attr":[
{
"attr_id":"3219",
"attr_name":"内存",
"attr_value_id":"370045",
"value_name":"64G"
},
{
"attr_id":"3220",
"attr_name":"颜色",
"attr_value_id":"370042",
"value_name":"绿色"
},
{
"attr_id":"3221",
"attr_name":"尺码",
"attr_value_id":"370040",
"value_name":"XL"
}
],
"sku_stock":1,
"sku_price":1,
"sku_org_price":1,
"vip_price":1,
"weight":"333",
}
]
.......
那么我们可以设置一个变量 goodsSkuList 或者skuList。存成这样的接口就OK了。
再看下界面效果
看完图之后,完全可以用一个变量skuList渲染。
然而之前的代码是如下
然后你在想。难道不能就用一个变量来搞定吗?而且specs[0]、specs[1],specs[2],为什么这么玩。而且这么写是限制了3个SKU。扩展性呢?
1.一个变量能搞定的事情,用了多个变量。变量多,复杂度就升级了。复杂度在于。变量修改的地方越多,就写的越多,比如一个变量搞定,只改一个变量,如果你用两三个实现一个变量能搞定的事情,岂不是,要操作三个?从一到三。所以尽可能少的变量。
图中的tableData是什么鬼?
tableData() {
var arr = this.specPrices
for (var i = 0; i < arr.length; i++) {
arr[i].spec0 = arr[i].specs[0]
arr[i].spec1 = arr[i].specs[1]
arr[i].spec2 = arr[i].specs[2]
}
return arr
}
那么specPrices是什么鬼?
好吧,当你追下去你还会发现,其他变量。因为添加规格,就会造成sku列表的联动。于是,又用了其他变量去实现。是不是很头疼。光看这些变量的变来变去就绕了。
2.扩展性没有了。固定了3个SKU。
于是会出现下面这种代码
<el-table-column :label="specs[0].type" prop="spec0"/>
<el-table-column v-if="specs[1]" :label="specs[1].type" prop="spec1"/>
<el-table-column v-if="specs[2]" :label="specs[2].type" prop="spec2"/>
computed: {
// 表格数据
tableData() {
var arr = this.specPrices
for (var i = 0; i < arr.length; i++) {
arr[i].spec0 = arr[i].specs[0]
arr[i].spec1 = arr[i].specs[1]
arr[i].spec2 = arr[i].specs[2]
}
return arr
}
},
以及sku组合的三层for循环以及对应的代码。
// 规格组合数组
specCombinations() {
var arrWra = []
var arr1, arr2, arr3, arr
// 有2个规格type
if (this.specs.length === 3) {
arr1 = this.specs[0].children
arr2 = this.specs[1].children
arr3 = this.specs[2].children
// 判断arr1是否为[], 如果是 为其添加个空字符串占位
if (arr1.length === 0) {
arr1 = ['']
}
if (arr2.length === 0) {
arr2 = ['']
}
if (arr3.length === 0) {
arr3 = ['']
}
arr = []
for (let t = 0; t < arr1.length; t++) {
for (let i = 0; i < arr2.length; i++) {
for (let k = 0; k < arr3.length; k++) {
arr = []
arr.push(arr1[t])
arr.push(arr2[i])
arr.push(arr3[k])
arrWra.push(arr)
}
}
}
return arrWra
// 只有1个规格type
} else if (this.specs.length === 2) {
arr1 = this.specs[0].children
arr2 = this.specs[1].children
// 判断arr1是否为[], 如果是 为其添加个空字符串占位
if (arr1.length === 0) {
arr1 = ['']
}
if (arr2.length === 0) {
arr2 = ['']
}
arr = []
for (let t = 0; t < arr1.length; t++) {
for (let i = 0; i < arr2.length; i++) {
arr = []
arr.push(arr1[t])
arr.push(arr2[i])
arrWra.push(arr)
}
}
return arrWra
// 只有1个规格type
} else if (this.specs.length === 1) {
arr = this.specs[0].children
if (arr.length === 0) {
arr = ['']
}
for (let i = 0; i < arr.length; i++) {
const _arr = []
_arr.push(arr[i])
arrWra.push(_arr)
}
return arrWra
} else if (this.specs.length === 0) {
return ['']
}
}
3.变量命名不够好。比之之前两点就不算啥了。
重构吧
看了这么一堆,你似乎有个结论,这种代码是堆叠式代码。有的堆叠不可怕,顶多冗余。有的堆叠就可怕了,简直是多个线交织。下面针对上面提到的前两点。
1.减少变量的使用
sku的列表。之前那么多变量实现的功能。我用了一个skuList。
看下页面代码。没有spec[0]、spec[1]、spec[2] 啥的。 没有tableData。自然就没有那个方法。也没有specPrices变量等其他有关之前的变量。
<el-table v-if="catAttrList.length !== 0 " :data="skuList" :row-class-name="tableRowClassName" style="max-width: 800px;">
<el-table-column v-for="(item, index) in skuList[0].sku_attr" v-if="skuList[0]" :key="index" :label="item.attr_name">
<template slot-scope="scope">
<div class="name-wrapper" >{{ scope.row.sku_attr[index].value_name }}</div>
</template>
</el-table-column>
2.扩展性。sku的表是根据属性组合的。我用了递归。这样即便将来扩展成多个SKU,代码不用动,而不是加for循环,加更多的spec[4]、5、6 啥的。
递归代码如下。较之上面的堆叠(三层for,还有其他for,还有更多if )好了很多。
assembleSkuList(data, skuAttr, arr, start) { // 递归组合sku: data 是从服务器获取的 属性list,skuArr是递归后组合的商品的skuList ,arr是不同规格一组,start 其实
if (data.length === start) { // 递归出口,几个规格就 start
return skuAttr
}
if (data[start].attr_value.length === 0) { // 因为规格 可以删除,这里为了健壮性要加默认值
const tempObj = {}
tempObj.attr_id = data[start].attr_id
tempObj.attr_name = data[start].attr_name
arr[start] = tempObj
}
// 下面是递归的主要逻辑
for (let i = 0; i < data[start].attr_value.length; i++) {
arr = deepClone(arr) // 深度拷贝,避免出错
const tempObj = data[start].attr_value[i]
tempObj.attr_id = data[start].attr_id
tempObj.attr_name = data[start].attr_name
arr[start] = tempObj // 几个规格就几个一组,用arr保存一小组
if (arr.length == data.length) {
skuAttr.push({
sku_attr: arr,
sku_org_price: this.batchElements.sku_org_price,
sku_price: this.batchElements.sku_price,
vip_price: this.batchElements.vip_price,
sku_stock: this.batchElements.sku_stock,
weight: this.batchElements.weight
})
}
if (data[start + 1] !== undefined) { // 如果有下一个规格就递归下去,因为 属性是多维
this.assembleSkuList(data, skuAttr, arr, start + 1)
}
}
return skuAttr
}
对比图
总结
1.一个变量能搞定的,不要用多个变量来实现相同的效果。一是复杂度上升。修改地方过多。二是,容易产生代码堆叠,代码冗余的堆叠不可怕,可怕的是交织。
2.扩展性。逻辑尽量的灵活。
3.命名。
4.当写的复杂的时候。尽量想想有没有其他方式(比如:api等等)或者逻辑。
比如商品分类的选择。
// 获取当前选中层级
getCurrentIndex(value) {
for (var i = 0; i < this.selectedArr.length; i++) {
for (var j = 0; j < this.selectedArr[i].optionArr.length; j++) {
if (this.selectedArr[i].optionArr[j].value === value) {
return i
}
}
}
}
当你遍历类目数组的时候,维度不就是层级么。也就是v-for的时候,index就是层级了,没必要自己算了。
参考资料:
《el-table-column 的嵌套v-for》https://blog.csdn.net/qq_28929589/article/details/79445354