前言
effect为副作用函数,也称为工作函数,本文主要对其功能进行模拟实现
一.完整代码
const reactMap = new WeakMap();
const ReactiveFlags = {
IS_REACTIVE : "isReactive"
}
let activeEffect = undefined;
class ReactiveEffect {
active = true;
deps = [];
constructor(fn) {
this.fn = fn
}
run() {
if(!this.active) {this.fn()};
try {
activeEffect = this;
return this.fn()
} finally {
activeEffect = undefined;
}
}
}
function effect(fn) {
const _effect = new ReactiveEffect(fn);
_effect.run()
}
// 将数据转化成响应式的数据,只能做对象的代理
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
}
const targetMap = new WeakMap()
function trigger(target,type,key,oldValue,value) {
const depsMap = targetMap.get(target);
if(!depsMap) return;
const effects = depsMap.get(key);
effects && effects.forEach(effect => {
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()))
}
let shouldTrack =!dep.has(activeEffect);
if(shouldTrack) {
dep.add(activeEffect)
activeEffect.deps.push(dep);
}
}
二. effect函数
let activeEffect = undefined;
class ReactiveEffect {
active = true;
deps = [];
constructor(fn) {
this.fn = fn
}
run() {
if(!this.active) {this.fn()};
try {
activeEffect = this;
return this.fn()
} finally {
activeEffect = undefined;
}
}
}
function effect(fn) {
const _effect = new ReactiveEffect(fn);
_effect.run()
}
effect的用法
effect(() => {})
特点及逻辑:
- effect在初始化的时候会立即执行一次;
- activeEffect为全局暴露,activeEffect绑定了effect里面的函数,以便于之后更新数据的时候调用
三. 依赖收集
effect中访问响应式数据属性时,会访问get,,所以依赖收集部分的代码逻辑在get函数中,也就是完整代码中的track函数
const targetMap = new WeakMap()
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()))
}
let shouldTrack =!dep.has(activeEffect);
if(shouldTrack) {
dep.add(activeEffect)
activeEffect.deps.push(dep);
}
}
targetMap返回格式
四. 响应式数据重新赋值之后effect中的函数重新执行
相关代码如下
function trigger(target,type,key,oldValue,value) {
const depsMap = targetMap.get(target);
if(!depsMap) return;
const effects = depsMap.get(key);
effects && effects.forEach(effect => {
effect.run()
})
}
- 因为在effect函数之后改变响应式数据之后,也会调用 set,所以if(!depsMap) return来优化;
- 触发effect.run()会重新触发effect里面的函数执行,所以可以达到更新渲染的效果