Vue源码探秘之 数据响应式原理

Vue源码探秘之 数据响应式原理

Vue源码探秘之 数据响应式原理_第1张图片

 

从MVVM模式说开去
模板

我{ {age}}岁了

数据变化
this.age++;
数据变化,视图会自动变化
Vue源码探秘之 数据响应式原理_第2张图片
 
侵入式和非侵入式
 
 
Vue源码探秘之 数据响应式原理_第3张图片
 
 
尤小右找到了“上帝的钥匙”
 
Object.defineProperty()
数据劫持 / 数据代理
 
利用 JavaScript 引擎赋予的功能,检测对象属性变化,仅有“上帝的钥匙”不够,还需要设计一套精密的系统
 
Object.defineProperty() 方法

Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

 

var obj = {};  
Object.defineProperty(obj, 'a', {
   value: 3
});
Object.defineProperty(obj, 'b', {
   value: 5
});
console.log(obj);
console.log(obj.a, obj.b);
Object.defineProperty() 方法可以设置一些额外隐藏的属性。
Object.defineProperty(obj, 'a', {
   value: 3,
   // 是否可写
   writable: false
});
Object.defineProperty(obj, 'b', {
   value: 5,
   // 是否可以被枚举
   enumerable: false
});
getter/setter
Vue源码探秘之 数据响应式原理_第4张图片
这里有一个小坑:用闭包存储 get set 的值
Object.defineProperty(obj, 'a', {
// getter
   get() {
   console.log('你试图访问obj的a属性');
},
// setter
   set() {
   console.log('你试图改变obj的a属性');
}
});
console.log(obj.a);
obj.a = 10;
getter/setter 需要变量周转才能工作
 
var temp;
Object.defineProperty(obj, 'a', {
// getter
   get() {
   console.log('你试图访问obj的a属性');
   return temp;
},
// setter
   set(newValue) {
   console.log('你试图改变obj的a属性', newValue);
   temp = newValue; }
});
使用 defineReactive 函数不需要设置临时变量了,而是用闭包
function defineReactive(data, key, val) {
Object.defineProperty(data, key, {
// 可枚举
   enumerable: true,
// 可以被配置,比如可以被delete
   configurable: true,
// getter
   get() {
   console.log('你试图访问obj的' + key + '属性');
   return val;
},
// setter
   set(newValue) {
   console.log('你试图改变obj的' + key + '属性', newValue);
   if (val === newValue) {
      return;
   }
   val = newValue;
   }
 });
}
递归侦测对象全部属性
 
Vue源码探秘之 数据响应式原理_第5张图片
 
数组的响应式处理
 
Vue源码探秘之 数据响应式原理_第6张图片
 
依赖收集
 
什么是依赖?
需要用到数据的地方,称为依赖
Vue1.x 细粒度 依赖,用到数据的 DOM 都是依赖;
Vue2.x 中等粒度 依赖,用到数据的 组件 是依赖;
getter 中收集依赖,在 setter 中触发依赖
Dep 类和 Watcher
 
• 把依赖收集的代码封装成一个Dep 类,它专门用来管理依赖, 每个 Observer 的实例, 成员中都有一个 Dep 的实例;
 
• Watcher是一个中介,数据发生变化时通过 Watcher 中转,通知组件
 
Vue源码探秘之 数据响应式原理_第7张图片
 
依赖就是 Watcher 。只有 Watcher 触发的 getter 才会收集依赖,哪个 Watcher触发了 getter ,就把哪个 Watcher 收集到 Dep 中。
 
Dep 使用发布订阅模式,当数据发生变化时,会循环依赖列表,把所 有的Watcher 都通知一遍。
 
代码实现的巧妙之处: Watcher 把自己设置到全局的一个指定位置, 然后读取数据,因为读取了数据,所以会触发这个数据的getter 。在getter中就能得到当前正在读取数据的 Watcher ,并把这个 Watcher 收集到Dep 中。
 
Vue源码探秘之 数据响应式原理_第8张图片
 
 
Vue源码探秘之 数据响应式原理_第9张图片
 
根据原理图来介绍整个流程: .

1、首先使用object. defineProperty()中的getter setter作为一个Observer (劫持器)去劫持data对象中的所有属性,在属性set的时候通知Dep (订阅者收集器)去通知相关订阅者。

2、实现一个Watcher (订阅者) , Watcher就是收到Dep数据变化的通知后,会去执行相对应的更新函数来更新视图,同一个数据可能在多处被使用,所以订阅者不止一个;这也是Dep存在的意义,对Watcher集中起来统-管理。
 
3.、Dep (订阅者收集器) ,里面存放每个数据对应的所有Watcher,当Observer 的set方法被触发时,就调用Dep里面的的notify (通知)方法,逐条去通知所有的Watcher。

4、comple是一个编译器,作用是解析模板指令,扫描和解析wue代码中每一个节点,先将节点转换为碎片化文档DocumentFragment (性能优化,减少重排) ,再-次性append所有节点至目标element内,完成视图的初始化;同时编译器还担当着初始化Watcher的任务,即给Watcher绑定相关的更新团数,最终使Watcher添加到Dep中去。
 
github源码地址: https://github.com/russ-gao/study-data-reactive

你可能感兴趣的:(Vue源码探秘,vue.js,typescript,javascript)