Vue2中响应式中的Vue.$set

Vue2中响应式中的Vue.$set

前言

在最近的项目中发现表格没有渲染出数据,最后发现是因为数据是非响应式的,由此引发的本文的编写。

Vue中的响应式

先看文档中的三段话:
1、当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。

2、受现代 JavaScript 的限制 (而且 Object.observe 也已经被废弃),Vue 无法检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。

3、Vue 实例的数据对象。Vue 将会递归将 data 的属性转换为 getter/setter,从而让 data 的属性能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个的 key/value 对):浏览器 API 创建的原生对象,原型上的属性会被忽略。大概来说,data 应该只能是数据 - 不推荐观察拥有状态行为的对象。
一旦观察过,不需要再次在数据对象上添加响应式属性。因此推荐在创建实例之前,就声明所有的根级响应式属性。

一般情况下,在Vue实例data选项中声明的对象,Vue在初始化时遍历data选项的属性对象,添加getter/setter和监听器,用于在某个属性值变化时触发监听更新界面。

所以在初始化完成之后给实例添加的属性如:
1、添加不存在的属性.name, ['name']
2、数组根据索引修改数据 [index]
此类方式没有触发添加目标对象的getter/setter操作,所以他们不是响应式的。
类似官网上的一段代码

var vm = new Vue({
  data:{
    a:1
  }
})

// `vm.a` 是响应式的

vm.b = 2
// `vm.b` 是非响应式的

那如何动态添加响应式属性呢?好在Vue给我们提供了方法。

Vue.set(this.someObject, newProperty, value)
vm.$set(this.someObject, newProperty, value)
this.someObject = Object.assign({}, this.someObject, sourceObject)

set

Vue提供了set$set让我们方便解决动态更改属性的问题,其中$setset的别名,让我们看看这个方法的源码,我加上了自己的注释:

/**
   * Set a property on an object. Adds the new property and
   * triggers change notification if the property doesn't
   * already exist.
   * 设置对象的属性。添加新属性并在属性不存在时触发更改通知
   *  返回成功添加的值
   */
  function set (target, key, val) {
  	//判断对象是不是为空 是不是原始类型的值
    if (isUndef(target) || isPrimitive(target)
    ) {
      warn(("Cannot set reactive property on undefined, null, or primitive value: " + ((target))));
    }
    //判断对象是不是数组  key是不是有效索引 即能不能转成整数
    if (Array.isArray(target) && isValidArrayIndex(key)) {
      //设置数组的长度 根据数组当前长度和设置的索引取两者中大的
      target.length = Math.max(target.length, key);
      //替换当前索引值
      target.splice(key, 1, val);
      return val
    }
    //判断对象自身和原型中有没有该属性 如果有直接赋值并返回
    //防止了重复设置getter/setter
    if (key in target && !(key in Object.prototype)) {
      target[key] = val;
      return val
    }
    //获取对象的观察者
    var ob = (target).__ob__;
    //判断对象是不是Vue实例 有观察者并且该实例使用次数大于0
    //对象是Vue实例 或者 已经在使用 发出错误信息
    //不能再运行时给Vue实例或其根$data添加响应式属性
    if (target._isVue || (ob && ob.vmCount)) {
      warn(
        'Avoid adding reactive properties to a Vue instance or its root $data ' +
        'at runtime - declare it upfront in the data option.'
      );
      return val
    }
    //如果没有观察者 说明该对象不是响应式的 简单属性赋值
    if (!ob) {
      target[key] = val;
      return val
    }
    //添加getter/setter
    defineReactive$$1(ob.value, key, val);
    //通知更新
    ob.dep.notify();
    //返回设置的值
    return val
  }

以上注释全是个人理解,其实刚开始在使用时疑问会不会添加多次getter/setter方法,然后就去看了源码,发现并不会重复添加,放心的同时,强行的把整个方法分析了一遍。
defineReactive$$1这个方法有兴趣可以看一下。

Object.assign()

set可以在添加一个属性的时候使用,此时并不会改变对象的引用。
如果要添加多个属性就要用Object.assign()
如文档中说的:
有时你可能需要为已有对象赋值多个新属性,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新属性不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的属性一起创建一个新的对象。

// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

不知道大家有没有疑问为什么
Object.assign(this.someObject, { a: 1, b: 2 })不是响应式,
Object.assign({}, this.someObject, { a: 1, b: 2 })是响应式。
其实他们的区别是目标对象的不同。
Object.assign(this.someObject, { a: 1, b: 2 })的目标对象是已经是响应式的this.somObject,在上边的set中我们知道通过vm.$setVue.set可以给响应式对象添加新的响应式属性,但是Object.assign并没有这些操作,所以添加的属性不是响应式的。
Object.assign({}, this.someObject, { a: 1, b: 2 })的目标对象是一个新对象,对象内存地址都是新的,所以下面代码

this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

就相当于更改响应式对象this.someObject的值,调用它的setter方法(开头三段话),此时Vue会遍历新对象的属性,添加getter/setter,从而实现添加多个属性并且是响应式的。

总结

  1. 根级响应式属性一定要在data中设置,才能响应式的添加属性或修改对象引用。
  2. Vue.set()/vm.set()可以方便的给响应式对象添加响应式属性。
  3. Object.assign()目标对象应设置为空对象,赋值的时候强行触发setter方法,达到添加多个响应式属性的目的。

参考文档:
深入响应式原理
Vue api 选项和数据 data

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值