前言
我们先顺一下vue使用响应式数据的流程:
vue 是通过 ref
和 reactive
来创建响应式值,改变响应式值,视图跟着发生变化。
我们今天就来看一下ref
和reactive
是如何实现的
准备
首先,打开ref
函数的位置
我们可以看到一个被ref
包裹的响应式数据,其实是一个RefImpl
对象
1. 创建ref
export function ref(value) {return createRef(value);
}function createRef(value) {const refImpl = new RefImpl(value);return refImpl;
}
可以看到创建一个ref对象的本质就是创建一个RefImpl
装饰对象
2. 编写RefImpl
对象
export class RefImpl {private _rawValue: any; // 原值private _value: any; // 代理值public dep; // 依赖数组:用来存放依赖public __v_isRef = true; // 区分是否是ref这个对象类型constructor(value) {this._rawValue = value;// 看看value 是不是一个对象,如果是一个对象的话// 那么需要用 reactive 包裹一下this._value = convert(value);this.dep = createDep(); // 创建effect对象}get value() {// 收集依赖trackRefValue(this);return this._value;}set value(newValue) {// 当新的值不等于老的值的话,// 那么才需要触发依赖if (hasChanged(newValue, this._rawValue)) {// 更新值this._value = convert(newValue);this._rawValue = newValue;// 触发依赖triggerRefValue(this);}}
}
❤️❤️ 为什么要有_rawValue
和_value
呢?
答:_value用来保存我们加工后的具有响应式的对象,
_rawValue
保存的是原始的值
3. 依赖收集和触发
依赖收集
// trackRefValue
export function trackRefValue(ref) {if (isTracking()) { // 判定师傅可以进行收集trackEffects(ref.dep);}
}// trackEffects
let activeEffect = void 0;export function trackEffects(dep) {// 用 dep 来存放所有的 effectif (!dep.has(activeEffect)) {dep.add(activeEffect);(activeEffect as any).deps.push(dep);}
}
activeEffect
:作用是用一个全局变量存储被注册的副作用函数
依赖触发
// ref.ts
export function triggerRefValue(ref) {triggerEffects(ref.dep);
}// effect.ts
export function triggerEffects(dep) {// 执行收集到的所有的 effect 的 run 方法for (const effect of dep) {if (effect.scheduler) {// scheduler 可以让用户自己选择调用的时机// 这样就可以灵活的控制调用了// 在 runtime-core 中,就是使用了 scheduler 实现了在 next ticker 中调用的逻辑effect.scheduler();} else {effect.run();}}
}
思考
- 用
ref
创建的值.value
有什么用?