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

1. 功能需求

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


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 应该如何解决

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

5. 解决方案

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

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

猜你喜欢

转载自blog.csdn.net/qq_33594380/article/details/79800939
今日推荐