vue响应式原理思考

参考: Vue数据响应式原理(写得非常好)

1、响应式原理(以下讲解大部分都是从这里copy过来的)

源码解读笔记代码
vue采用数据劫持结合发布者-订阅者模式的方式实现数据的响应式,使用Object.defineProperty()把data下面的数据设置为set/get方式,数据get的时候收集订阅者,set的时候通知发布更新

原理解释主要涉及5个概念,如下图(图片也是参考这里:Vue数据响应式原理
图片参考:https://www.jianshu.com/p/1032ecd62b3a
图中红色的箭头表示的是收集依赖时获取数据的流程。Watcher会收集依赖的时候(这个时机可能是实例创建时,解析模板、初始化watch、初始化computed,也可能是数据改变后,Watcher执行回调函数前),会获取数据的值,此时Observer会拦截数据(即调用get函数),然后通知Dep可以收集订阅者啦。Dep将订阅数据的Watcher保存下来,便于后面通知更新。

图中绿色的箭头表示的是数据改变时,发布更新的流程。当数据改变时,即设置数据时,此时Observer会拦截数据(即调用set函数),然后通知Dep,数据改变了,此时Dep通知Watcher,可以更新视图啦。

data和view就是字面意思,主要讲解Observer、Dep、watcher
Observer: 数据的观察者,让数据对象的读写操作都处于自己的监管之下。当初始化实例的时候,会递归遍历data,用Object.defineProperty来拦截数据(如果数据是多维数组,或者数组里面是对象这种多层嵌套的模式,那么每一层的数据都会被定义)。
但是需要注意的是,如果是数组实际上observer是没有定义get和set的,参见如下源码:
在这里插入图片描述

Dep:数据更新的发布者,get数据的时候,收集订阅者,触发Watcher的依赖收集;set数据时发布更新,通知Watcher 。(这个dep的使用就是在Object.defineProperty的get和set里面,参考源码defineReactive$$1函数)
Watcher:数据更新的订阅者,订阅的数据改变时执行相应的回调函数(更新视图或表达式的值)。
一个Watcher可以更新视图,如html模板中用到的{{test}},也可以执行一个$watch监督的表达式的回调函数(Vue实例中的watch项底层是调用的$watch实现的),还可以更新一个计算属性(即Vue实例中的computed项)。

因为js基础欠缺,有些原理我有点一知半解,如果想要看更加详细的代码讲解参考: Vue数据响应式原理 ,也可以结合我跑过的 Vue数据响应式原理加强理解

2、几个问题

1. 为什么数组下标修改非数组元素,修改数组len不响应
定义数组arr = [1,2,3],如果修改arr[0]=999是不会再页面响应的
因为defineReactive没有定义数组元素的get\set(get\set里面加了watcher的依赖),所以如果修改数组元素是不会感知到的,但是其实使用defineProperty是可以实现数组元素的监测的,看下面代码就实现了对每个元素修改的监测(参考:vue为什么不能检测数组的变化):

function defineReactive(data, key, value) {
    Object.defineProperty(data, key, {
      enumerable: true,
      configurable: true,
      get: function defineGet() {
        console.log(`get key: ${key} value: ${value}`)
        return value
      },
      set: function defineSet(newVal) {
        console.log(`set key: ${key} value: ${newVal}`)
        value = newVal
      }
    })
  }
  function observe(data) {
    Object.keys(data).forEach(function(key) {
      defineReactive(data, key, data[key])
    })
  }

  let arr = [1, 2, 3]
  observe(arr)

在控制台设置arr[0]=10的时候,会发现进入了set函数:
在这里插入图片描述
但是为什么vue不实现,看github有人提问尤作者,他的回答是:
在这里插入图片描述
可以理解,应该是数组在实际开发中元素大的时候,如果使用defineProperty对每个元素进行监听就太消耗性能了,而如果想要实现对数组的某个元素进行修改实际上可以使用方法splice(这里的splice在vue中是有重写的,具体参考文章:https://www.jianshu.com/p/1032ecd62b3a)

2. 如果数组元素是对象可以通过下标修改

数组objArr = [1,{k1,‘v1’}],修改:objArr[1].k1 = ‘vAfter’,这个修改可以响应
查看源码会发现对于数组中的元素如果是数组或者对象还会调用observe函数,vue源码如下:
在这里插入图片描述
最后也使用defineReactive$$1设置了set,get,所以修改也会被响应

3. 如果删除或者新增数组元素,不管数组元素是否是对象,都不会响应

对于(2)中的数组,如果执行:objArr[1]={kAfter:‘vAfter’},这样是不会被响应的,该objArr[1]就是看objArr有没有set/get了,但其实数组在vue中没有给数组设置set/get

4. 如何修改数组某个下标非对象的元素,如果修改len

官方给出了说法https://cn.vuejs.org/v2/guide/reactivity.html#%E6%A3%80%E6%B5%8B%E5%8F%98%E5%8C%96%E7%9A%84%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9 使用vm.$set(vm.items, indexOfItem, newValue)修改下标值
如果要修改len,可以直接使用splice

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值