计算属性,功能和vue2.x基本等同,本文主要对其功能进行模拟实现
const reactMap = new WeakMap();
const ReactiveFlags = {
IS_REACTIVE : "isReactive"
}
let activeEffect = undefined;
class ReactiveEffect {
active = true;
deps = [];
constructor(fn,scheduler) {
this.fn = fn;
this.scheduler = scheduler;
}
run() {
if(!this.active) {this.fn()};
try {
activeEffect = this;
return this.fn()
} finally {
activeEffect = undefined;
}
}
}
class ComputedRefImpl {
effect;
_value;
dep = new Set();
_dirty = true;
_v_isReadOnly = true;
_v_isRef = true;
constructor(getter,setter) {
this.effect = new ReactiveEffect(getter, () => {
// 稍后依赖的属性变化会执行此调度函数
if(!this._dirty) {
this._dirty = true;
triggerEffect(this.dep)
}
})
}
get value() {
trackEffect(this.dep)
if(this._dirty) {
this._dirty = false;
this._value = this.effect.run()
}
return this._value
}
}
function effect(fn) {
const _effect = new ReactiveEffect(fn);
_effect.run();
// computed新增
const runner = _effect.run.bind(_effect);
runner.effect = runner;
return runner
}
// 将数据转化成响应式的数据,只能做对象的代理
function reactive(target) {
if(!(typeof target === 'object' && target !== null)) {
return;
}
if(target[ReactiveFlags.IS_REACTIVE]) {
return target
}
let exisProxy = reactMap.get(target);
if(exisProxy) {
return exisProxy
}
const proxy = new Proxy(target,{
get(target,key,receiver) {
if(key === ReactiveFlags.IS_REACTIVE) {
return true
}
track(target,'get',key)
return Reflect.get(target,key,receiver)
},
set(target,key,value,receiver) {
let oldValue = target[key];
let result = Reflect.set(target,key,value,receiver);
if(oldValue !== value) {
trigger(target,'set',key,oldValue,value)
}
return result
}
});
reactMap.set(target,proxy)
return proxy
}
// 计算属性
function computed(getterOrOptions) {
let onlyGetter = typeof getterOrOptions === 'function'
let getter;
let setter;
if(onlyGetter) {
getter = getterOrOptions;
setter = () => {console.warn('no set')}
} else {
const {get,set} =getterOrOptions;
[getter,setter] = [get,set]
}
return new ComputedRefImpl(getter,setter)
}
const targetMap = new WeakMap()
function trigger(target,type,key,oldValue,value) {
const depsMap = targetMap.get(target);
if(!depsMap) return;
const effects = depsMap.get(key);
triggerEffect(effects,'effects')
}
function triggerEffect(effects) {
effects && effects.forEach(effect => {
if(effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
})
}
function track(target,type,key) {
if(!activeEffect) return ;
let depsMap = targetMap.get(target);
if(!depsMap) {
targetMap.set(target,(depsMap = new Map()))
}
let dep = depsMap.get(key);
if(!dep) {
depsMap.set(key,(dep = new Set()))
}
trackEffect(dep)
}
function trackEffect(dep) {
if(activeEffect) {
let shouldTrack =!dep.has(activeEffect);
if(shouldTrack) {
dep.add(activeEffect)
activeEffect.deps.push(dep);
}
}
}
const target ={name: 'sandy',age: 18,height: 195};
const r1 = reactive(target);
const formatData = computed (() => {
const {name,age} = r1;
return `format123 ${name},${age}`
})
effect(() => {
const { value } = formatData;
console.log(`effect ${value}`)
})
setTimeout(() => {
r1.name = 'wendy'
},2000)
// setTimeout(() => {
// console.log(formatData.value,'formatData.value')
// },3000)
为的是方便于当监听值改变后,自定义逻辑代码
//声明
constructor(getter,setter) {
this.effect = new ReactiveEffect(getter, () => {
// 稍后依赖的属性变化会执行此调度函数
if(!this._dirty) {
this._dirty = true;
triggerEffect(this.dep)
}
})
}
//调用
function triggerEffect(effects) {
effects && effects.forEach(effect => {
if(effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
})
}
const target ={name: 'sandy',age: 18,height: 195};
const r1 = reactive(target);
const formatData = computed (() => {
const {name,age} = r1;
return `format123 ${name},${age}`
})
effect(() => {
const { value } = formatData;
console.log(`effect ${value}`)
})
setTimeout(() => {
r1.name = 'wendy'
},2000)
声明了_dirty属性,_dirty默认为true,当_dirty为true时,则表明需重新计算,为false时,不需要重新计算。
当属性值改变时,在调度器中将_dirty设置为true,表示之后访问时需要重新计算;
当访问了计算属性的value之后,表示已经访问过了,不需要重新计算,将_dirty设置为false