effect实现
标签:function,const,target,depsMap,effect,源码,key,vue3 From: https://www.cnblogs.com/dgqp/p/17365708.html
定义
effect
方法:export function effect(fn, options: any = {}) { // 创建响应式的effect const effect = createReactiveEffect(fn, options); // 默认会让effect先执行一次 if (!options.lazy) { effect(); } return effect; } let uid = 0; let activeEffect; // 存储当前的effect const effectStack = []; // 保存effect 保证依赖关系 function createReactiveEffect(fn, options) { // 返回响应式的effect const effect = function reactiveEffect() { // 保证effect没有加入到effectStack中 if (!effectStack.includes(effect)) { try { effectStack.push(effect); activeEffect = effect; return fn(); } finally { effectStack.pop(); activeEffect = effectStack[effectStack.length - 1]; } } }; effect.id = uid++; // 制作一个effect表示,用于区分effect effect._isEffect = true; // 用于表示这个是响应式effect effect.raw = fn; // 保留effect对应的原函数 effect.options = options; // 在effect上保存用户属性 return effect; }
当
effect
函数执行时,进行取值操作,让属性记住对应的effect
函数。function createGetter(isReadonly = false, shallow = false) { return function get(target, key, receiver) { // ... if (!isReadonly) { // effect函数执行时,进行取值操作,让属性记住对应的effect函数 track(target, TrackOpTypes.GET, key); } } }
需要构建
track
方法进行依赖收集// 可以拿到当前的effect const targetMap = new WeakMap(); export function track(target, type, key) { // 此属性不需要收集,因为没有在effect使用 if (activeEffect === undefined) { 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())); } if (!dep.has(activeEffect)) { dep.add(activeEffect); activeEffect.deps.push(dep); } }
trigger
触发更新对新增属性和修改属性做分类,在
setter
里面function createSetter(shallow = false) { return function set(target, key, value, receiver) { const oldValue = target[key]; const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key); const result = Reflect.set(target, key, value, receiver); if (!hadKey) { // 新增属性 trigger(target, TriggerOpTypes.ADD, key, value) } else if (hasChanged(value, oldValue)) { // 修改属性 trigger(target, TriggerOpTypes.SET, key, value, oldValue) } return result; } }
触发
effect
找到依次执行,trigger
方法。// 找属性对应的effect让其执行(数组,对象) export function trigger(target, type, key?, newValue?, oldValue?) { // 如果这个属性没有,收集过依赖,那不需要做任何操作 const depsMap = targetMap.get(target); if (!depsMap) return; // 所有的要执行的effect全部存放到一个新的集合中,最终一起执行 const effects = new Set(); // 去重 const add = (effectsToAdd) => { if (effectsToAdd) { effectsToAdd.forEach((effect) => effects.add(effect)); } }; // 1.看修改的是不是数组的长度,因为修改长度影响比较大 if (key === "length" && isAarray(target)) { // 如果对应的长度有依赖收集需要更新 depsMap.forEach((dep, key) => { // 如果更改的长度小于收集的索引,那么这个索引也需要触发effect重新执行 if (key === "length" || key > newValue) { add(dep); } }); } else { // 可能是对象 // 这里是修改,不能是新增 if (key !== undefined) { add(depsMap.get(key)); // 如果实现新增 } // 如果修改数组中的某一个索引 switch (type) { case TriggerOrTypes.ADD: if (isAarray(target) && isIntegerKey(key)) { add(depsMap.get("length")); } } } effects.forEach((effect: any) => effect()); }