首页
归档
笔记
树洞
搜索
友言

文章详情

Interesting People Record Interesting.

/ JavaScript / 文章详情

面试被问到用Vue手写一个类似淘宝类型功能,直接懵了

Sonder
2022-08-16
11042字
28分钟
浏览 (2.9k)

具体功能

如下gif:

10bedf754fc9665cb6a344d33aec1137.gif

需求设计

差点凌乱,结果一想,这难吗? 难就难在没有思路。于是这时的我两腿一蹬,大脑开始高速运转,生成如下流程图:

image.png

这么看来好像就清晰很多了,果然设计是编程中不可或缺的一步!

正式coding

首先我问面试官,后台响应回来的详情数据是什么结构,于是他给了我这么一坨

复制代码
mockData: {
   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里面存放是三个维度的分类,实现效果差不多是这样

image.png

,所以问题不大,我们先把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

image.png

逻辑处理及代码优化

面试官在告知我需求的时候,强调又强调数据不能写死,聪明的我当然明白,意思就是要尽可能让我们的前端代码有更高的复用性,说白了就是可能会匹配颜色、尺码、面料,也可能会用金额、风格来匹配。

设计一个万能属性匹配器

可以想象,不管他是什么属性,但只要是【条件对象】的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]);
    }
  },
  • 注意注意
  1. 第2行是讲具体商品数据中的所有属性名存储到数组中
  2. 第7行则是动态添加响应式属性,这是Vue2中动态添加属性必须要做的,否则更改数据页面那个属性不变化,Vue3这个问题已经解决

点中后的激活效果

复制代码
<a :class="{ active: condition[types[index]] === item }"
        href="#"
        v-for="(item, i) in rowArr"
        :key="i"
        >{{ item }}</a>
      >

这个比较简单!

核心代码(过滤数据)

  1. 首先我们给a标签添加点击事件,点了那项,就更改其中颜色或者尺码或者面料的值,因此相同函数我们必须要知道点了哪个类别,这里我们可以用【外层的index】做区分,给a标签加上事件
复制代码
<a @click="changeData(types[index], item)" a>{{item}}</a>
  1. 保存这个数据变化
复制代码
 changeData(prop, data) {
      let self = this;
      self.condition[prop] = data;
 }
  1. 有了数据,就可以做过滤显示了,computed是最佳人选
复制代码
 computed: {
    showGoodsInfo() {
      return his.mockData.json2.filter((e) => {
        return diffObjectByKeys(this.condition, e);
      });
    },
  }
  1. 核心的工具函数,对比两个对象的函数还没实现,我们去实现它,这个函数接收俩对象,对比其属性值,全都相同,返回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;
}
  1. 别忘了使用这个计算属性:
复制代码
价格是: {{ showGoodsInfo }}

最终效果

f1659a23b31a68d0809cd5e7d2ab698c.gif

完整代码

HelloWorld.vue,这是个组件

复制代码
<template>
   <div class="hello">
       <div v-for="(rowArr, index) in mockData.json1" :key="index">
           <span :class="{ active: condition[types[index]] === item }" class="goods-item" v-for="(item, i) in rowArr" :key="i" @click="changeData(types[index], item)">{{item}}</span>
       </div>
       所选的:{{ showGoodsInfo }}
       <button @click="isShow1 = !isShow1">结算</button>
       <MsgBox :show.sync="isShow1" :conf="{showGoodsInfo:showGoodsInfo[0]}"/>
   </div>
</template>

<script>
// https://blog.csdn.net/tc1124692/article/details/123814949
import MsgBox from "@/components/MsgBox";
// 核心的工具函数,对比两个对象的函数还没实现,我们去实现它,这个函数接收俩对象,对比其属性值,全都相同,返回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;
       return false;
   }
   return isEqual;
}
export default {
   components: {
       MsgBox
   },
   data() {
       return {
           isShow1: false,
           types: [], // 保存从json2中获取的动态属性名称
           condition: {}, // 有json1.length个属性,分别是前三项color、type、mianliao
           mockData: {
               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,
                   },
               ]
           }
       }
   },
   computed: {
       showGoodsInfo() {
           return this.mockData.json2.filter((e) => {
               return diffObjectByKeys(this.condition, e);
           });
       },
   },
   methods: {
       //过滤数据
       changeData(prop, data) {
           let self = this;
           self.condition[prop] = data;
       },

   },
   created(){
       //模拟created中动态获取,并动态生成types和condition
       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]);
       }
   },
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.hello .goods-item {
   text-decoration: none;
   border: 1px solid grey;
   margin: 8px;
   padding: 6px;
   display: inline-block;
   color: grey;
}
.hello .goods-item.active {
   border: 1px solid red;
   color: red;
}
</style>

MsgBox.vue,这也是个组件

复制代码
<script>
export default {
   props: {
       show: {
           type: Boolean,
           default: false
       },
       conf: {
           type: Object
       }
   },
   // 组件为了更轻便,可以考虑去除组件内的生命周期以及 DOM 结构。让组件更优化,取舍 this
   functional: true, // 函数式组件
   render(h, context) {
       const {type, color, price} = context.props.conf.showGoodsInfo;
       const {show} = context.props; // 是否显示当前组件
       let update = context.listeners['update:show']; // 显示当前组件的事件
       return (
           <transition name="fade">
               {show && (
                   <div>
                       <h2>颜色:{color}</h2>
                       <div>尺寸:{type}</div>
                       <div>价格:{price}</div>
                       <button onClick={ () => update(false) }>关闭</button>
                   </div>
               )}
           </transition>
       )
   }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.fade-enter-active, .fade-leave-active {
   transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
   opacity: 0;
}
</style>

本文转自 https://blog.csdn.net/tc1124692/article/details/123814949,如有侵权,请联系删除。

下一篇 / js中简单入门算法

🎯 相关文章

💡 推荐文章

🕵️‍♂️ 评论 (0)