东窗事发
之前去阿里的二面,信心满满的我准备了一堆面试题,想着可以暴打面试官了,很是开心!结果去了以后,他喵的居然让我给他现场机试实现一个功能,当场蒙蔽了我!
具体功能如下gif:
需求设计
差点凌乱,结果一想,这难吗? 难就难在没有思路。于是这时的我两腿一蹬,大脑开始高速运转,生成如下流程图:
这么看来好像就清晰很多了,果然设计是编程中不可或缺的一步!
正式coding
首先我问面试官,后台响应回来的详情数据是什么结构,于是他给了我这么一坨
{
json1: [
["红色", "黄色", "蓝色"],
["S", "M"],
["棉的", "涤纶"],
],
json2: [
{
color: "红色",
type: "S",
mianliao: "棉的",
price: 100,
},
{
color: "红色",
type: "M",
mianliao: "棉的",
price: 200,
},
{
color: "红色",
type: "S",
mianliao: "涤纶",
price: 300,
},
{
color: "红色",
type: "M",
mianliao: "涤纶",
price: 400,
},
{
color: "黄色",
type: "S",
mianliao: "棉的",
price: 500,
},
{
color: "黄色",
type: "M",
mianliao: "棉的",
price: 600,
},
{
color: "黄色",
type: "S",
mianliao: "涤纶",
price: 700,
},
{
color: "黄色",
type: "M",
mianliao: "涤纶",
price: 800,
},
{
color: "蓝色",
type: "S",
mianliao: "棉的",
price: 900,
},
{
color: "蓝色",
type: "M",
mianliao: "棉的",
price: 1000,
},
{
color: "蓝色",
type: "S",
mianliao: "涤纶",
price: 1100,
},
{
color: "蓝色",
type: "M",
mianliao: "涤纶",
price: 1200,
},
]
}
复制代码
我们从这个结构不难看出,json2就是具体的数据,而json1里面存放是三个维度的分类,实现效果差不多是这样 ,所以问题不大,我们先把json1的数据渲染出来再说。
<div v-for="(rowArr, index) in mockData.json1" :key="index">
<a v-for="(item, i) in rowArr" :key="i" @click="changeData(index,item)">{{item}}</a>
</div>
复制代码
上述数据我们保存在mockData中,只遍历json1,效果出来了,适当加点样式
#app a {
text-decoration: none;
border: 1px solid grey;
margin: 8px;
padding: 6px;
display: inline-block;
color: grey;
}
#app a.active {
border: 1px solid red;
color: red;
}
复制代码
出来了,beautiful
逻辑处理及代码优化
面试官在告知我需求的时候,强调又强调数据不能写死,聪明的我当然明白,意思就是要尽可能让我们的前端代码有更高的复用性,说白了就是可能会匹配颜色、尺码、面料,也可能会用金额、风格来匹配。
设计一个万能属性匹配器
可以想象,不管他是什么属性,但只要是【条件对象】的abc属性,与【数据对象】的abc属性一致,我就可以认为筛选出来的就是这个对象,因此不要在乎abc这个名,岂不就通用性很高啦。接着我们在data下声明一个condition对象来存储当前选中的各项属性,再给他默认值。
先回顾一下数据结构
data() {
return {
types:[], // 保存从json2中获取的动态属性名称
condition: {}, // 有json1.length个属性,分别是前三项color、type、mianliao
mockData: {
json1: [
["红色", "黄色", "蓝色"],
["S", "M"],
["棉的", "涤纶"],
],
json2: [
{
color: "红色",
type: "S",
mianliao: "棉的",
price: 100,
}
/*....省略数据*/
]
}
}
}
复制代码
模拟created中动态获取,并动态生成types和condition
created(){
this.types = Object.keys(this.mockData.json2[0]);
for (let i = 0; i < this.types.length; i ++) {
// 由于分类有可能没有数据的属性多,所以判断一下
if (!this.mockData.json1[i])return;
let propName = this.types[i];
this.$set(this.condition,propName,this.mockData.json1[i][0]);
}
},
复制代码
- 注意注意
- 第2行是讲具体商品数据中的所有属性名存储到数组中
- 第7行则是动态添加响应式属性,这是Vue2中动态添加属性必须要做的,否则更改数据页面那个属性不变化,Vue3这个问题已经解决
点中后的激活效果
<a :class="{ active: condition[types[index]] === item }"
href="#"
v-for="(item, i) in rowArr"
:key="i"
>{{ item }}</a>
>
复制代码
这个比较简单!
核心代码(过滤数据)
- 首先我们给a标签添加点击事件,点了那项,就更改其中颜色或者尺码或者面料的值,因此相同函数我们必须要知道点了哪个类别,这里我们可以用【外层的index】做区分,给a标签加上事件
<a @click="changeData(types[index], item)" a>{{item}}</a>
复制代码
- 保存这个数据变化
changeData(prop, data) {
let self = this;
self.condition[prop] = data;
}
复制代码
- 有了数据,就可以做过滤显示了,computed是最佳人选
computed: {
showGoodsInfo() {
return his.mockData.json2.filter((e) => {
return diffObjectByKeys(this.condition, e);
});
},
}
复制代码
- 核心的工具函数,对比两个对象的函数还没实现,我们去实现它,这个函数接收俩对象,对比其属性值,全都相同,返回true,否则false。
function diffObjectByKeys(obj1, obj2) {
let isEqual = true;
for (let key in obj1) {
let v = obj1[key];
if (obj2[key] && obj2[key] === v) {
continue;
} else return false;
}
return isEqual;
}
复制代码
- 别忘了使用这个计算属性:
价格是: {{ showGoodsInfo }}
复制代码
最终效果
非常的Nice!!