vue变化检测机制

vue根据状态自动进行渲染dom;

vue 声明式

vue 对比react 和 angular(变化检测)

变化检测方式无非分成了两种(根据粒度的粗细可以划分)

1.拉,react 和 angular 就是拉对于可能的状态变化,数据变化就会通知框架,对于所有的组件进行暴力对比dom,采用虚拟dom进行暴力对比,找出需要渲染的dom节点,然后重新渲染; > 脏检查;

2.推 vue使用的 vue1.0 的时候是以dom节点的方式更新,当状态变化的时候会通知所有的依赖的dom节点,通知dom节点开始更新;

​ vue2.0的时候是组件的形式作为通知单位,当检测到状态变化的时候出现,会通知组件,之后借助虚拟dom,组件内部进行比对;通知到具体的dom节点开始更新;

​ 以dom节点作为基本单位的更新开销太大,占用内存太大;

如何追踪状态变化;

使用object.defineproperty监听数据波动触发对应的事件处理函数;专门监听对象数据变化;

Object.defineProperty 是vue中双向绑定的基础。vue是通过数据劫持的方式来做数据绑定的,最核心的方法是通过 Object.defineProperty()方法来实现对属性的劫持,达到能监听到数据的变动。要实现数据的双向绑定,

demo:

var obj = {};
var initValue = 'hello';
Object.defineProperty(obj,"newKey",{
    get:function (){
        //当获取值的时候触发的函数
        return initValue;    
    },
    set:function (value){
        //当设置值的时候触发的函数,设置的新值通过参数value拿到
        initValue = value;
    }
});
//获取值
console.log( obj.newKey );  //hello

//设置值
obj.newKey = 'change value';

console.log( obj.newKey ); //change value

当从obj中获取key =newkey的数据的时候触发 get函数
	设置对应的key的数据的时候触发set函数;
	基于此实现双向绑定;

vue2.0中数据变化只是把通知发送到组件;然后组件内部通过虚拟dom进行比对;

依赖收集

当检测到数据对象的属性变化的时候必须要通知使用到这个对象的组件,所以就需要依赖收集:

依赖收集: 就是把所有引用这个数据的组件全部放到一个数组中存储收集起来; 之后当通知组件的时候只需要把数组中的组件循环遍历通知一下;

因为组件中使用数据必须从对应的对象中提取数据,这样就会触发get方法,所以只要是组件引用了这个数据那么一定会触发get方法所以在get方法中收集依赖;

同理当数据变化,也就是对数据重新赋值,所以会触发set方法;所以在set中通知所有依赖此数据的组件;



# 其实最终依赖此数据的所有组件都存储在 以 key 作为名字的数组中;
get 收集依赖
set 触发通知依赖;
就是在 get方法中把组件加入到数组中
set方法中循环遍历数组调用 notify通知函数通知所有的钻进;
target中就是触发事件的所有组件;

target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素、文档或窗口。

因为依赖这个数据的组件各种各样类型不同所以为了更有效的进行依赖检测,进行有效的封装操作;

创建一个单独的类 watcher 专门负责通知其他组件,相当一个中介转发的作用;首先通知watcher之后由watcher 通知其他组件;一个中间类;一个组件(依赖)对应一个watcher实例,添加一个依赖就是添加一个wacther实例

实现思路:其实就是把watcher实例加入到依赖数组中,之后在set方法中调用watcher中的方法;组件数据发生变化之后watcher通知各个组件;执行watcher中的回调函数;

组件更新与否取决于wacther通知不通知;

以上所说都是针对obj的单属性对应的数组;

如果想要对obj所有的属性,子属性实现依赖检测;需要封装一个observer检测类;

作用: 把一个obj传到observer中之后那么这个obj就会变成响应式的object,obj的所有属性以及字属性都会变成get set的方式,所有的属性变化都会通知相应的依赖;

实现思路:获取obj中的所有key 对于每一个key都调用 definereactive(),因为obj是一个递归层层加深的结构,所以为了保证如果属性对应也是一个对象,其内部的属性也变成set get方法所以在definereactive()方法内部

​ 执行

​ if(typeof val ==‘object’){

​ new Observer(val); # 如果这个子属性对应的也是一个对象,把这个对象再次放到observer类中,递归挂载,这样所有的子属性也可以转化成get set方法;

}

通过把obj递归放入oberver类中的方式保证所有的属性以及子属性都可以进行变化检测;

使用getter/setter方法进行变化追踪的问题:

因为vue只是通过 definereactive的方式把属性转化成 get set来追踪数据变化;但是getset方法只可以追踪一个数据是否被修改,无法对于新增属性或者是删除属性进行追踪,所以在obj中增加属性或者是删除属性都不会通知对应组件进行重新渲染,不会触发通知,所以

​ 执行 this.infos.name = “123” delete this.infos.name 不会触发通知;

所以这种方式有的数据变化也是检测不到;

event.target target返回的是触发事件的标签名字;

其实最终放到依赖数组中的是组件对应的watcher实例


版权声明:本文为weixin_44164270原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。