Vue 爬坑之旅 -- 双层 v-for 中使用 v-model 绑定 input 时碰到的坑

这是我在开发时碰到的一个问题,具体场景是这样的:
有一个比较复杂的表单填写和提交页面,在页面初始化时有一个门票数组(数据是在上一个页面存在 store 里的),这个数组里面的每个元素都是一个门票对象,然后请求后端接口,后端会返回一个报名需要回答的问题数组。

在拿到问题数组后,需要将问题数组里面的每个元素处理一下之后,将处理过后的问题数组放到门票数组里面,每个门票内都需要有一个问题数组。每个问题数组里面都有需要填写的表单

这样的话,最终需要渲染的数据就变成了一个二维数组的形式,要将二维数组的数据渲染到页面上就需要用到双层 v-for 循环。而在内层 v-for 里面会有很多表单项都是需要绑定 v-model 的。

整个需求场景就是上面说的那样,这个需求看起来只是稍微有点复杂,也不算非常复杂,拿到需求后就开始撸码了,初始代码大概如下:

//请求服务器数据,拿到数据后将它传给 processData 方法进行处理
let res = await this.api.meeting.getSignUpQuestions(this.meetingId)
   if (res && res.data && res.data.success && res.data.result) {
     this.processData(res.data.result)
   }

	processData (data) {
        this.tickets.map(ticket =>{
          for (let item of data) {
            if (item.type === 1) {
              //如果选项是手机号,则自动将当前用户的手机号填入
              if (this.$store.state.userInfo && this.$store.state.userInfo.loginname) {
                item.fieldVal = this.$store.state.userInfo.loginname
              }
            }
            if (item.type === 4 && item.fieldVals.length > 0) {
              this.processRadioList(item)
            }
            if (item.type === 5 || item.type === 8 && item.fieldVals.length > 0) {
              this.processCheckboxList(item)
            }
          }
          
          ticket.fieldMsg = fieldMsg
        })
      },

经过上面那一通处理之后,页面是正常渲染出来了,但是当我测试表单数据输入时发现问题来了,当我在其中一个门票的 input 内输入内容时,其它门票的同名 input 也会出现同样的内容。

What the fuck ,在仔细检查了几遍代码没发现问题后,曾一度怀疑是 vue 本身的问题,但又转念一想,双层 v-for 加 v-model 应该不是非常罕见的场景,vue 不可能会有这种问题啊,在 Google 一顿无果后,又转回来检查代码,当检查到 processData 方法时,突然想到我是直接拿请求后的数据过来处理的,那么在给每个门票对象放入问题数组时,其实用的都是同一个对象,这样就导致了 v-model 都是绑定到了同一个对象上,就会导致上面的问题。

知道原因了,解决方案也就出来,就是在处理问题数据时,先将数据拷贝下,不要直接使用,因为我这里的数据就是一个 json 对象,所以我就直接用 json 的转化方法将它转化为字符串传给 processData 处理,修改后的代码如下

//请求服务器数据,拿到数据后将它传给 processData 方法进行处理
let res = await this.api.meeting.getSignUpQuestions(this.meetingId)
   if (res && res.data && res.data.success && res.data.result) {
     this.processData(JSON.stringify(res.data.result))
   }

	processData (data) {
        this.tickets.map(ticket =>{
        let fieldMsg = JSON.parse(data)
          for (let item of fieldMsg) {
            if (item.type === 1) {
              //如果选项是手机号,则自动将当前用户的手机号填入
              if (this.$store.state.userInfo && this.$store.state.userInfo.loginname) {
                item.fieldVal = this.$store.state.userInfo.loginname
              }
            }
            if (item.type === 4 && item.fieldVals.length > 0) {
              this.processRadioList(item)
            }
            if (item.type === 5 || item.type === 8 && item.fieldVals.length > 0) {
              this.processCheckboxList(item)
            }
          }
          
          ticket.fieldMsg = fieldMsg
        })
      },

因为以前开发时基本没有使用过对象拷贝,都是直接拿数据来用,经过这次事件之后,让我认识到,在一些稍微复杂点的场景下,数据处理需要谨慎,最好不要直接使用,用之前最好是先拷贝或者用 json 转化下,以避免此类问题。

你可能感兴趣的:(Vue)