基本的 ref 目标
it('happy path', () => {const a = ref(1);expect(a.value).toBe(1);});
实现
class RefImpl {private _value: any;constructor(value) {this._value = value;}get value() {return this._value;}}export function ref(value) {return new RefImpl(value);}
ref 响应性目标
it('should be reactive', () => {const a = ref(1);let dummy;let calls = 0;effect(() => {calls++;dummy = a.value;});expect(calls).toBe(1);expect(dummy).toBe(1);a.value = 2;expect(calls).toBe(2);expect(dummy).toBe(2);// same value should not triggera.value = 2;expect(calls).toBe(2);expect(dummy).toBe(2);});
实现
class RefImpl {private _value: any;// 用于收集依赖的容器public dep;constructor(value) {this._value = value;this.dep = new Set();}get value() {return this._value;}set value(newValue) {}}export function ref(value) {return new RefImpl(value);}
会用到 effect 收集依赖的部分,因为 ref 只有一个 value,对 effect 的 track 局部抽离为 trackEffect
同理触发依赖也是对 effect 的 trigger 局部抽离为 triggerEffect
export function track(target, key) {if (!isTracking()) return;let depsMap = targetMap.get(target);if (!depsMap) {depsMap = new Map();targetMap.set(target, depsMap);}let dep = depsMap.get(key);if (!dep) {dep = new Set();depsMap.set(key, dep);}// 抽离依赖收集trackEffect(dep);}export function trackEffect(dep) {// 看盾 dep 之前有没能有添加过,添加过就不添加if (dep.has(activeEffect)) return;dep.add(activeEffect);activeEffect.deps.push(dep);}export function trigger(target, key) {const depsMap = targetMap.get(target);const dep = depsMap.get(key);// 抽离触发依赖triggerEffect(dep);}export function triggerEffect(dep) {for (const effect of dep) {if (effect.scheduler) {effect.scheduler();} else {effect.run();}}}export function isTracking() {return shouldTrack && activeEffect !== undefined;}
import { trackEffects, triggerEffect } from './effect';class RefImpl {private _value: any;// 用于收集依赖的容器public dep;constructor(value) {this._value = value;this.dep = new Set();}get value() {// 依赖收集// 如果没有 effect 过会 trackEffects 中的 activeEffect 会为 undefined// 这里先作一个判断if (isTracking()) {trackEffects(this.dep);}return this._value;}set value(newValue) {// 如果 set 的时候,新旧值相同就不作处理if(Object.is(newValue, this._value)) return;this._value = newValue;// 触发依赖triggerEffects(this.dep);}}
重构
set value(newValue) {// 如果 set 的时候,新旧值相同就不作处理if(hasChange(newValue, this._value) {this._value = newValue;// 触发依赖triggerEffects(this.dep);}}
export const hasChange = (val, newValue) => {return !Object.is(val, newValue);};
get value() {// 依赖收集trackRefValue(this);return this._value;}
function trackRefValue(ref) {// 如果没有 effect 过会 trackEffects 中的 activeEffect 会为 undefined// 这里先作一个判断if (isTracking()) {trackEffect(ref.dep);}}
嵌套目标
it('should make nested properties reactive', () => {const a = ref({count: 1,});let dummy;effect(()=>{dummy = a.value.count;});expect(dummy).toBe(1);a.value.count = 2;expect(dummy).toBe(2);});
实现
class RefImpl {private _value: any;private _rawValue: any;public dep;constructor(value) {this._rawValue = value;// 看看 value 是不是对象this._value = isObject(value) ? reactive(value) : value;this.dep = new Set();}get value() {trackRefValue(this);return this._value;}set value(newValue) {// 这里对比会有问题,为因为会转为 proxy,所以要保留未 reactive 的对象// if (hasChange(newValue, this._value)) {if (hasChange(newValue, this._rawValue)) {// 保留未 reactive 的对象this._rawValue = newValue;this._value = isObject(newValue) ? reactive(newValue) : newValue;triggerEffect(this.dep);}}}
重构
抽离 isObject(value) ? reactive(value) : value
class RefImpl {private _value: any;private _rawValue: any;public dep;constructor(value) {this._rawValue = value;// 看看 value 是不是对象this._value = covert(value);this.dep = new Set();}get value() {trackRefValue(this);return this._value;}set value(newValue) {// 这里对比会有问题,为因为会转为 proxy,所以要保留未 reactive 的对象// if (hasChange(newValue, this._value)) {if (hasChange(newValue, this._rawValue)) {// 保留未 reactive 的对象this._rawValue = newValue;this._value = covert(newValue);triggerEffect(this.dep);}}}function convert(value) {return isObject(value) ? reactive(value) : value;}
