欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Vue: 批量更新操作实现与踩坑记录

程序员文章站 2022-05-25 17:06:08
...

1. 功能需求

从后端获取到的房间列表包含房间号、房间类型、房间人数、性别四个参数。用户在列表中可以修改任意记录的任意参数,点击保存修改的时候将修改提交到服务器,实现批量更新。

Vue: 批量更新操作实现与踩坑记录

2. 组件树结构

Page

|--LocationText

|--SideBar

|--MainContent

    |--HeadLine

    |--RoomUnit(一个单元包含一条完整的记录)

    |--BottomButton

3. 实现思路

3.1 列表初始化

  • MainContent 中发送ajax 请求获取房间及其信息列表
GetRoomInfo () {
  this.apiGet('/admin/room', {})
    .then((res) => {
      console.log(res)
      if (res.errcode == 0) {
        this.roomList = res.data
      } else {
        alert('获取用户列表失败!')
      }
    }, (err) => {
      console.log(err)
    })
}
  • 使用v-for 遍历的到的结果,并传值给RoomUnit 子组件
<RoomUnit
  class="unit-margin"
  v-for="(item, index, key) in roomList"
  @updateRoom="UpdateRoom"
  :item="item"
  :index="index"
  :key="key"></RoomUnit>
  • RoomUnit 接收父组件传值并初始化数据
props: {
  item: {
    type: Object
  }
}

3.2 监听列表修改

  • RoomUnit 组件监听item 对象的值,监测到修改时通知父组件
watch: {
  // 监测item的值是否被修改
  item: {
    handler: function (newV, oldV) {
      this.$emit('updateRoom', newV)
    },
    deep: true
  }
}
  • 父组件接收子组件传来的值,并将其添加至待批量更新的json 对象中
UpdateRoom (...data) {
  let id = data[0].id
  this.updateRoomList[id] = data[0]
}

3.3 批量更新

  • 发送多次ajax 请求对待更新json 对象中的记录依次进行更新
SaveChange () {
  let allUpdated = true
  // count计数
  let count = 0
  // 获取对象长度
  let len = Object.getOwnPropertyNames(this.updateRoomList).length - 1
  // 批量更新
  for (let index in this.updateRoomList) {
    this.apiPost('/admin/room/update/' + index, this.updateRoomList[index])
      .then((res) => {
        // 操作成功
        if (res.errcode == 0) {
          count++
          // 最后一次操作时候判断是否成功并刷新页面
          if (count == len) {
            if (allUpdated) {
              this.$message({
                message: '更新成功',
                type: 'success'
              })
              // 重新获取列表数据,刷新页面
              this.GetRoomInfo()
              // 清空upade列表
              this.updateRoomList = {}
            } else {
              this.$message.error('更新失败,请稍后重试')
            }
          }
        } else {
          allUpdated = false
        }
      }, (err) => {
        console.log(err)
      })
  }
}

4. 潜在Bug

以上方案基本上已经可以解决批量更新的问题了。

但是,可能会有一个潜在的bug,是否会出现这个bug 跟后端程序员的写法有关。

4.1 产生bug的原因

会产生这个bug 的原因在于:mysql 针对一次没有任何内容变化的更新操作会返回结果0(正常是1)

也就是说,如果前端提交的更新列表里面包含了未修改的记录,而后端程序员又将结果为0判断为更新失败,那么就会产生bug。

4.2 前端为什么会提交未经修改的数据,我明明已经在每次更新成功之后将待更新列表清空了

在3.3 的批量更新过程中我们可以看到,每次确定更新成功以后会重新发送ajax 请求获取房间列表,刷新RoomUnit 组件列表。RoomUnit 刷新之后会触发其中的watch 事件,导致所有的item 记录都被添加到父组件的updateRoomList 中(此处是因为所有item 的地址都发生了变化)。用户这个时候如果再点击更新,那么实际上向服务器发送的是所有未经修改的记录,因此就会报错,更新失败!

4.3 应该如何解决

(1)方案一:既然每次刷新之后updateRoomList 之中会填充所有数据,那么我在刷新之后将updateRoomList 重新清空一下就好了。考虑到ajax 的异步问题,使用setTimeOut 设定时间,确保在ajax 请求完成之后再清空列表。于是SaveChange 局部就变成了以下的写法

if (res.errcode == 0) {
  count++
  // 最后一次操作时候判断是否成功并刷新页面
  if (count == len) {
    if (allUpdated) {
      this.$message({
        message: '更新成功',
        type: 'success'
      })
      // 刷新页面
      this.GetRoomInfo()
      // 清空upade列表
      // this.updateRoomList = {}
      setTimeout(function () {
        for (let key in this.updateRoomList) {
          delete this.updateRoomList[key]
        }
      }, 500)
    } else {
      this.$message.error('更新失败,请稍后重试')
    }
  }
} else {
  allUpdated = false
}

代码怎么看都没问题,可是还是有bug,updateRoomList 中的数据清不掉!!为什么会清不掉,delete 操作没毛病啊,而且还是在ajax 请求完成之后进行的。查了半天,原来是this 指向的问题。setTimeOut 中设置一层function 之后,this 已经不再指向Vue 实例,而是指向它所在的function,所以当然清不掉Vue 中的updateRoomList了。

解决办法,修改setTimeOut 的写法

setTimeout(() => {
  for (let key in this.updateRoomList) {
    delete this.updateRoomList[key]
  }
}, 500)

(2)方案二:既然刷新列表后,watch 是因为地址的变化导致父元素列表加满,那我们在watch 中判断一下新旧值的地址是否相同就好了,如果相同,发生了变化才通知父元素,如果不相同,则不通知。

5. 解决方案

综上所述,最后的解决方案跟第3点实现思路中的方案极为相似,只有一处代码不同,RoomUnit 中watch 监听的写法

watch: {
  // 监测item的值是否被修改
  item: {
    handler: function (newV, oldV) {
      // 判断是否是刷新导致的变化,地址相同说明不是刷新
      if (newV == oldV) {
        this.$emit('updateRoom', newV)
      }
    },
    deep: true
  }
}