接上一篇文章# vue3 reactive和watchEffect的简单实现
ref的实现
import { isTracking, reactive, trackEffect, triggerEffect } from "./index";
class RefImpl {
_value: any;
deps;
__v_isRef = true;
constructor(raw) {
this.deps = new Set();
this._value = convert(raw);
}
get value() {
if (isTracking()) {
trackEffect(this.deps);
}
return this._value;
}
set value(value) {
if (value === this._value) return;
this._value = convert(value);
triggerEffect(this.deps);
}
}
const convert = data => {
return typeof data === 'object' ? reactive(data) : data;
}
export const ref = (raw) => {
return new RefImpl(raw);
}
export const isRef = (raw) => {
return raw.__v_isRef ? true : false
}
export const unRef = (raw) => {
return isRef(raw) ? raw.value : raw;
}
ref的单测
describe('ref', () => {
it('happy path', () => {
const a = ref(1);
expect(a.value).toBe(1);
})
it('should be reactive', () => {
const a = ref(1);
let dummy;
let calls = 0;
effect(() => {
calls++;
dummy = a.value;
})
expect(dummy).toBe(a.value);
expect(calls).toBe(1);
a.value = 2;
expect(calls).toBe(2);
expect(calls).toBe(2);
a.value = 2;
expect(calls).toBe(2);
expect(calls).toBe(2);
})
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)
})
it('isRef', () => {
const a = ref(1);
const user = reactive({ foo:1 });
expect(isRef(a)).toBe(true);
expect(isRef(true)).toBe(false);
expect(isRef(user)).toBe(false);
})
it('unRef', () => {
const a = ref(1);
expect(unRef(a)).toBe(1);
})
})
computed的实现
import { ReactiveEffect } from "./index";
class ComputedImpl {
_dirty: any = true;
rawValue;
activeEffect: ReactiveEffect;
constructor(private getter) {
this.activeEffect = new ReactiveEffect(getter, () => {
this._dirty = true;
});
}
get value() {
if (this._dirty) {
this.rawValue = this.activeEffect.run();
this._dirty = false;
}
return this.rawValue;
}
}
export const computed = (getter) => {
return new ComputedImpl(getter);
}
computed的单测
import { computed } from "../computed";
import { reactive } from "../index"
describe('computed', () => {
it('happy path', () => {
const user = reactive({
age: 10
})
const age = computed(() => user.age);
expect(age.value).toBe(10);
})
it('should compute lazily', () => {
const value = reactive({ foo: 1 });
const getter = jest.fn(() => {
return value.foo
})
const cValue = computed(getter);
expect(getter).not.toHaveBeenCalled();
expect(cValue.value).toBe(1)
expect(getter).toHaveBeenCalledTimes(1);
cValue.value
expect(getter).toHaveBeenCalledTimes(1);
value.foo = 2;
// expect(getter).toHaveBeenCalledTimes(1);
// expect(cValue.value).toBe(2)
})
})