阅读 7759

结合实际场景聊聊大部分前端都不会使用的数据结构Map

前言

大家好呀,我是wangly,一名前端菜猫子。
最近项目中很多地方都用到了Map来做数据结构。所以总结一篇文章来复习自己。顺便给各位看官总结下这个ES6新的数据结构。它不像Object一样在开发中大量的被用到。但在某些特定的场景下使用它简直就是神来一笔。所以今天就给各位唠嗑下Map了。如果你还不知道Map是什么,不妨在掘金看其他的笔者写的文章。当然,直接看下去也是可以的。

如果觉得不错,看完后可以点个赞哦,支持下笔者哦。

什么是Map

MapES6新增加的数据结构。它属于键控“集团”(keyed collections)的股东之一,它会用户提供了更加强大的key键扩展集。更加自由的容器隐私,如set,get,size一系列的原生api,来方便用户管理容器。一定情况下,它们可以提供相当大的性能优势。

何时使用Map代替Object

  • Object的key无法支持该数据时
  • 需要了解对象大小时
  • 不想存在冲突的key值时
  • 自定义key值时

推荐阅读这篇文章,了解一些基本的知识:
前端小智:何时使用 Map 来代替普通的 JS 对象

一些常用的业务场景

都是一些真实的业务场景,非常简单。如果你的项目中碰到不妨可以试试这种解决方案。我就以vue作为演示环境来给各位做Demo详解啦。其它环境下的朋友借鉴思路哦。以下三个案例都可以用Object实现。只是写起来会有点异样性。

场景一:个人信息表

在一些Admin项目中我们通常都对个人信息进行展示,比如将如下信息展示到页面上,你会怎么做呢?立马一个糟糕的写法就出来了。

<div class="info-item">
  <span>姓名</span>
  <span>{{info.name}}</span>
</div>
<div class="info-item">
  <span>年龄</span>
  <span>{{info.age}}</span>
</div>
<div class="info-item">
  <span>性别</span>
  <span>{{info.sex}}</span>
</div>
<div class="info-item">
  <span>手机号</span>
  <span>{{info.phone}}</span>
</div>
<div class="info-item">
  <span>家庭住址</span>
  <span>{{info.address}}</span>
</div>
<div class="info-item">
  <span>家庭住址</span>
  <span>{{info.duty}}</span>
</div>

</div>
复制代码
mounted() {
  this.info = {
    name: 'wangly',
    sex: '男',
    age: '18',
    phone: '13000000000',
    address: '中国......',
    duty: '总经理'
  }
}
复制代码

我们通过Map来改造,将我们需要显示的labelvalue存到我们的Map后渲染到页面。虽然在javascript中代码显得沉淀。只是做一个演示。具体操作大家可以一起思考。如何去解决更好一点。这只是我的一些看法。虽然逻辑层代码多了。但是view视图却节省了很多耦合的代码。以啊就是我对cell的一个简单理解。你还有更妙的解决方案吗?

<template>
  <div id="app">
    <!-- <router-view></router-view> -->
    <div class="info-item" v-for="[label, value] in infoMap" :key="value">
      <span>{{label}}</span>
      <span>{{value}}</span>
    </div>
  </div>
</template>
复制代码
data: () => ({
  info: {},
  infoMap: {}
}),
mounted () {
  this.info = {
    name: 'wangly',
    sex: '男',
    age: '18',
    phone: '13000000000',
    address: '中国......',
    duty: '总经理'
  }
  const mapKeys = ['姓名', '性别', '年龄', '电话', '家庭地址', '身份']
  const result = new Map()
  let i = 0
  for (const key in this.info) {
    result.set(mapKeys[i], this.info[key])
    i++
  }
  this.infoMap = result
}
复制代码

那么该如何渲染到页面呢?

map在v-for下也变得非常现实。可以看下面代码。一步搞定

<!-- show map  -->
<div class="info-item" v-for="[label, value] in infoMap" :key="value">
  <span>{{label}}</span>
  <span>{{value}}</span>
</div>
复制代码

唠嗑

通过map遍历,在解构出想要的key, value,就可以将一些完整的键对展示内容展示到页面上了。这是我常用的一个地方。当然它使用计算属性的方式也可以做到很好的完成。

场景二:自定义提交表单

不知道大家有没有碰过前端动态表单。意思是通过组合而成的表单。比如:

  • A表单字段: ['A', 'B', 'C', D, 'E']
  • B表单字段: ['A', 'C', D, 'E']
  • C表单字段: ['A', 'B', D, 'E', 'F']

根据不同形势下上传的表单,它不一样。如果要渲染到页面上,容易出现空白列。因为它没有数据,你不清楚你当前的表单是什么样子的。基本场景: 学生,老师,工作人员的疫情调查表,它们是不一样的。它们都需要交到某个管理这个事的工作人员手上。那么这个工作人员查看表单信息是多元化的。就拥有多个模板。甚至还需要做导出xls文件。所以传统表单的prop就很难去适应这个多元的东西了。

模板结构

list对象是不一样的。这先当与它们传上来的字段表。我们需要通过labelvalue渲染到页面上。

模板一:
模板二:

动态表格

结果一:
结果二:

实现过程

其实核心就是map.set(child.label, child.value),将后台发下来自定义模板数组,转换为Map。这样,我们就不要过多的去关注一些模板的字段。可以专注数据的渲染。

<table class="table" border="1" id="table">
  <tr>
    // 获取头部,也就是map的key
    <th v-for="([label], key) in tableMap.length > 0 ? tableMap[0] : []">
      {{label}}
    </th>
  </tr>
  // 渲染map数组
  <tr v-for="(map, index) in tableMap">
    <td v-for="[,value] in map">{{value}}</td>
  </tr>
</table>
复制代码
createModule () {
  this.tableMap = []
  this.tableList.forEach(el => {
    const map = new Map()
    map.set('时间', el.name)
    el.custom.forEach(child => {
      // 将label 作为 key , value作为值
      map.set(child.label, child.value)
    })
    // map结束
    this.tableMap.push(map)
  })
}
复制代码

场景三:避免重复的数据

动态更新最常见的地方就是移动端的下拉加载更多。正常下是不会出错的。但是有一种极为特殊的情况。socket实时推送的累加的内容。我们都知道socket在发送消息的时候,存在通信延迟。影响这方面的地方很多。往往无法保证在socket接收到这个对象添加到DOM时,用户有没有手动更新这个消息内容。如果列表中存在。你在socket.onmessage动态添加到列表就会有值冲突的问题。虽然可以通过filter过滤出去。但是通过mapkey无重复性。可以很好的完成这个问题。

当button添加前后

可以看到,页面上出现了一条新的标签9。但是浏览器报错key重复了。因为v-for我是使用id作为key的,有两条相同的数据在视图上,这是不合理的现象。如果我们使用map,就完全不用去担心这个事情。我们无论set多少次都不会有相同的数据影响到重复的key,新的key会替换旧的数据。 旧的 新的

<div class="info-item" v-for="[key, {id, label}] in tableMap" :key="key">
  <span>{{id}}</span>
  <span>{{label}}</span>
</div>
<button @click="pushTable">添加标签9</button>

复制代码
// @ pushTbale function 
pushTable () {
  this.tableMap.set(9, {
    id: 9,
    label: '标签10'
  })
  this.tableMap = new Map(this.tableMap)
},

// createData @moount function
const map = new Map()
for (let index = 0; index < 10; index++) {
  map.set(index, {
    id: index,
    label: `标签${index}`,
  })
}
this.tableMap = map

复制代码

心得

  • Vue对于Map的响应监听比较差,当你直接set,数据更新了。但是无法通知观察者更新视图。当你需要响应更新,只能被动重新设置当前整个Map,这其实是不合理的。
  • Map和Object是两个东西,Map能做到Object有时候做不到的东西,
  • 合理使用Map,思考是否需要用到它。
  • 不要被单一原则固定思维,思考是否有更好的方式解决问题。(div走遍天,object一招鲜)
  • Map的使用场景很小,但是不要忽视它。

后言

如果觉得对你有帮助,可以给我点赞哦。各位的支持是对我最好的肯定。个人其他文章推荐。文章内容不一定是最好的解决方法。

v-model源码分析: 点击跳转
前端五月面经: 点击跳转

关注下面的标签,发现更多相似文章
评论