响应式初步探究(primitive value)

143 阅读3分钟

响应式理解初步

什么是响应式?先来看一段代码:

let a = 10;
let b = a + 10;
​
console.log(b);             // 20a = 20;
console.log(b);             // 20

这段代码中,我们声明了两个number类型变量,b这个变量依赖于a。

现在我们的需求是:当 a 发生改变, b 自动也相应发生改变,这就是响应式数据。

但是在上面这个代码中,我们发现 b 并没有随之改变。因此也就没有实现响应式。

其实,最简单的办法就是每当 a 发生改变, 我们人为地调用 b = a + 10,但作为精明的程序员,这显得也太愚蠢了。

响应式原理探究

这里我们先探究基本数据类型来实现响应式。

之前我们提到过,在上面的代码中,b 是依赖于 a 的, 而这个依赖关系是 b = a + 10; 那么可以想到的思路是:当我们重新设置 a 的值的时候,我们重新调用一遍这个依赖关系。

但是我们可以预想到的是:在复杂庞大的项目中,依赖关系可不止这么简简单单。

因此这里提出一个概念叫依赖收集:将和基变量有联系的依赖关系存储到一个集合中;当我们重新给基变量重新赋值的时候,我们依次调用所有的依赖关系,这叫做依赖触发

最简单的响应式原理模型(基本数据类型)

基于上面的原理探究,我们其实可以设想到的是:将一个基本数据类型包装成一个类,通过这个类中的set和get方法来对这个变量的设置进行拦截!而在这个拦截过程中,我们就可以触发这些依赖

代码:

// 这个就是对基本数据类型来转换成响应式对象的一个包装类
class Dep {
    // 构造器函数接受一个initial value,作为一个初始值
    constructor(initVal) {
        this.effects = new Set();       // 这个set集合作为依赖的容器
        this._val = initVal;            // 给this._val赋值,_val意味“私有”变量
    }
​
    get value() {                       // 获取dep.value
        return this._val;
    }
​
    set value(newV) {                   // 当对dep.value赋值的时候会调用这里的函数
        this._val = newV;               // 重新赋值
        this.notify();                  // 调用notify
    }
​
    depend(effect) {                    // 收集依赖
        this.effects.add(effect);
    }
​
    notify() {                          // 触发已经收集的依赖
        this.effects.forEach((effect) => {
            effect(this);
        });
    }
}
​
function effectWatch(effect, dep) {     // 定义依赖的方法
    effect(dep);                        // 初次调用依赖关系
    dep.depend(effect);                 // 添加依赖
}
​
​
// 响应式例子:
const a = new Dep(10);
​
let b;                          // 声明一个变量,依赖于a
effectWatch((a) => {
    b = a.value + 20;           // 依赖关系
    console.log(b);             // 打印b
}, a);
​
a.value = 25;
a.value = 40
​
​
// 30           调用effectWatch时打印
// 45           将a.value设置成25时打印
// 60           将a.value设置成40时打印

调用逻辑:

初始化a为一个Dep(响应式)对象 -----> 定义依赖变量b -----> 调用effectWatch来定义依赖关系和初始化b -----> a.value 重新赋值 -----> 调用 set value 方法 -----> 重新赋值 -----> 调用notify,触发已经收集的依赖。