Vue 爬坑之旅 -- 修改了数据,界面为啥没反应?

在日常开发中,有时候会碰到修改了数据而界面上没有反应的情况,这种情况我相信很多人都碰到过,这时候可能很多人都会想到通过 $forceUpdate 方法来让页面强制更新,这个方法大部分时候都可以解决问题。
问题看上去是解决了,但其实这个方法并不是最正确和推荐使用的方式。要想彻底解决问题并且以后不再犯同样的错误,我们必须知道导致问题产生的原因才行,首先看一下下面的代码:

<template>
  <div>
    <div>
      <span>用户名: {{ userInfo.name }}</span>
      <span>用户性别: {{ userInfo.sex }}</span>
      <span v-if="userInfo.phoneNum">
        手机号: {{ userInfo.phoneNum }}
      </span>
    </div>
    <button @click="addPhoneNum">添加手机号</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      userInfo: {
        name: '张三',
        sex: '男'
      }
    }
  },
  methods: {
    addPhoneNum() {
      this.userInfo.phoneNum = '13888888888'
    }
  }
}
</script>

在上面的代码中,我们希望给用户信息里面添加手机号属性,但是通过this.userInfo.phoneNum = ‘13888888888’ 添加之后,并没有生效,这是为什么呢?

这是因为在Vue内部,数据响应是通过使用Object.definePrototype监听对象的每一个键的getter,setter方法来实现的,但通过这种方法只能监听到已有属性,新增的属性是无法监听到的,但我就是想监听,小编你说咋办吧。下面小编提供了四种方式,如果有更多方式,欢迎下方评论区告诉我。
vue 官方文档 中也有相关的说明

Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。
您还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:
有时你可能需要为已有对象赋值多个新 property,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。

了解了上面的知识后,那么要怎么处理这个问题也就有方法了

解决修改了对象属性后界面不刷新问题的方法

1. 将本来要新增的属性提前在data中定义好

比如上面的手机号,我可以提前在userInfo里面定义好,这样就不是新增属性了,就像下面这样

data() {
    return {
      userInfo: {
        name: '张三',
        sex: '男',
        phoneNum : ''
      }
    }
  }

2. 直接替换掉userInfo

虽然无法给userInfo里面添加新的属性,但是因为userInfo已经定义好了,所以我直接修改userInfo的值不就可以了么,所以也可以像下面这样写

this.userInfo = {
  // 将原来的userInfo 通过扩展运算法复制到新的对象里面
  ...this.userInfo,
  // 添加新属性
  phoneNum : '13888888888'
}

3. 使用Vue.set (推荐使用)

其实上面两种方法都有点取巧的嫌疑,其实对于新增属性,Vue官方专门提供了一个新的方法Vue.set用来解决新增属性无法触发数据响应。
上面的代码使用Vue.set可以修改为

import Vue from 'vue'

// 在这里添加用户的公众号
addPhoneNum() {
  Vue.set(this.userInfo,'phoneNum', '13888888888')
}

但是每次要用到set方法的时候,还要把Vue引入进来,好麻烦,所以为了简便起见,Vue又将set方法挂载到了Vue的原型链上了,即Vue.prototype. s e t = V u e . s e t , 所 以 在 V u e 组 件 内 部 可 以 直 接 使 用 t h i s . set = Vue.set,所以在Vue组件内部可以直接使用this. set=Vue.set,Vue使this.set代替Vue.set

this.$set(this.userInfo,'phoneNum', '13888888888')

有许多人不知道什么时候应该用Vue.set,其实只有当你要赋值的属性还没有定义的时候需要使用Vue,set,其他时候一般不会需要使用。

4. 使用$forceUpdate

$forceUpdate 的存在,让许多前端开发者不会再去注意数据双向绑定的原理,因为不论什么时候,反正修改了data之后,调用一下 $forceUpdate 就会让Vue组件重新渲染,bug是不会存在的。但是实际上这个方法并不建议使用,因为它会引起许多不必要的性能消耗。

针对数组的特定方式

其实不仅仅是对象,数组也存在数据修改之后不响应的情况,比如下面这段代码

<template>
  <div>
    <ul>
      <li v-for="item in list" :key="item">
        {{ item }}
      </li>
    </ul>
    <button @click="changeName">修改名称</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: ['张三', '李四']
    }
  },
  methods: {
    changeName() {
      this.list[0] = '王五'
    }
  }
}
</script>

上面的代码希望将张三的名字修改为王五,实际上这个修改并不能生效,这是因为:

Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:this.list[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:this.list.length = newLength

所以在上例中通过this.list[0] = ‘王五’ 是无法触发数据响应的,那应该怎么办呢?像上面提到的Vue.set和$forceUpdate都可以解决这个问题,比如Vue.set可以这样写

Vue.set(this.list,0,'王五')

除了那些方法之外,Vue还针对数组提供了变异方法

在操作数组的时候,我们一般会用到数据提供的许多方法,比如push,pop,splice等等,在Vue中调用数组上面提供的这些方法修改数组的值是可以触发数据响应的,比如上面的代码改为以下代码即可触发数据响应

this.list.splice(0,1,'王五')

实际上,如果Vue仅仅依赖getter与setter,是无法做到在数组调用push,pop等方法时候触发数据响应的,因此Vue实际上是通过劫持这些方法,对这些方法进行包装变异来实现的。

Vue对数组的以下方法进行的包装变异:
push
pop
shift
unshift
splice
sort
reverse
所以在操作数组的时候,调用上面这些方法是可以保证数据可以正常响应,

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值