一、响应式模块的作用
响应式模块是vue框架最核心的功能,vue中的watch、computed、模板更新都是基于响应式模块实现的。
响应式模块可以做到响应式对象中的某些属性发生变化时,自动执行这些属性依赖的函数,实现自动更新
二、响应式模块的组成
响应式模块由reactive响应式对象和ReactiveEffect组成
-
reactive响应式对象
通过new Proxy(源对象、控制器)创建响应式对象,访问该对象的所有属性都会调用控制器中的get方法,设置该对象的所有属性都会调用控制器中的set方法。
调用get方法时,会进行依赖收集
调用set方法时,会进行依赖更新 -
ReactiveEffect 类
作用:把用户传进来的逻辑封装起来,在合适的时机(响应式数据更新时)去调用。
参数:
1.fn:调用的时候会收集依赖(为了解决函数中的分支问题,收集依赖之前会做一次清理操作,把之前收集的依赖先清理掉)。
2.scheduler: (响应式数据更新时)去调用,如果没传这个参数会调用fn。
属性:
1.fn: 用户创建实例的时候传进来的函数
2.scheduler: 用户创建实例的时候传进来的调度函数
3.active:Boolean 标记当前ReactiveEffect对象是否激活状态,如果不是激活状态不会进行依赖收集(默认激活)
4.parent:(null或ReactiveEffect对象) 记录嵌套的effect中的上一级
5.deps:Array 记录当前ReactiveEffect对象在哪里被收集过(属性收集的依赖会使用一个set集合存储,deps这个属性就是用来存储包含当前ReactiveEffect对象的集合的地址)。清除依赖的时候会将当前的ReactiveEffect对象从deps里面的所有集合中删除。
方法:
1.run: 执行用户传进来的fn。
代码:
run() {
if (!this.active) {
return this.fn();
} // 如果当前是未激活状态 不进行依赖收集
try {
// 这里需要进行依赖收集 ,将当前的effect和稍后用到的属性关联起来
this.parent = activeEffect; // 记录上一级effect,第一次为null
activeEffect = this;
// 执行函数之前先清理effect,再重新收集 解决分支问题
cleanupEffect(this);
return this.fn();
} finally {
activeEffect = this.parent;
this.parent = null;
}
}
这个函数要做的事情:
①判断this.active是否等于false :如果是说明当前的effect不是激活状态,不需要进行依赖收集:直接调用this.fn()并retun,不往下执行
②this.parent指向全局变量activeEffect(如果当前ReactiveEffect对象调用run方法时是在其他ReactiveEffect对象的run方法中执行的,this.parent就会记录上一级的ReactiveEffect对象。等到依赖收集完成(也就是调用完this.fn)之后全局变量activeEffect指向this.parent。解决了effect嵌套问题);
③activeEffect指向this(下一步调用this.fn时,相关属性会收集activeEffect作为依赖)
④调用this.fn (此时如果在这个函数中访问了某个响应式对象的某些属性,就会调用这个响应式对象中预先设置好的get函数,这时候就会在get函数中进行依赖收集)
⑤activeEffect指向this.parent
⑥this.parent指向null(这一步其实可以不做,因为下次调用run会对this.parent重新赋值)
- effect函数
作用:创建一个ReactiveEffect实例,返回一个可以直接执行当前ReactiveEffect实例中的run方法的函数(这个函数一般用来手动触发更新)。默认先执行一次ReactiveEffect实例的run方法进行依赖收集,如果依赖属性发生变化就会触发更新
参数:
fn: 用户传进来的立即执行函数
option: 对象类型:一般包含scheduler属性(调度函数)
这个函数要做的事情:
① 创建一个ReactiveEffect实例(用一个_effect变量接收)
②执行ReactiveEffect实例的run方法(进行依赖收集)
③定义一个runner函数(this绑定_effect)
④返回runner函数
代码:
export function effect(fn, options: any = {}) {
// fn可以根据数据变化重新执行
const _effect = new ReactiveEffect(fn, options.scheduler);
_effect.run();
const runner = _effect.run.bind(_effect);
runner.effect = runner;
return runner;
}
- 依赖收集
作用: 将某个响应式对象中的某个属性与activeEffect创建关联关系。
存储关联关系的数据结构:
用一个WeakMap映射表存储对象中的某些属性和effect的依赖关系,如下图:
其中:weakMap中的键代表某个对象,值是一个Map映射表,这个映射表反映该对象某些属性和effect的绑定关系(键代表属性,值代表effect集合)
- 触发更新
三、核心原理
使用reacticve创建响应式对象时,会对该对象进行代理并返回一个Proxy代理对象。在创建Proxy对象时,会传入handler对象,handler对象声明了代理对象的get和set等操作。
访问该代理对象的某个属性时,会进入handler中的get函数,