目录

Vue.js设计与实现(第三章)— 响应式之解决自增栈溢出

本章是解决响应式系统中的出现自增时,导致的栈溢出。

栈溢出的情况:

当输入一下代码会出现栈溢出

javascript 复制代码
  effect(() => {
        console.log(obj.foo++);
  });

分析:

通过trigger debugger 可以看到每次都会重复进行依赖收集然后触发渲染,并且 effectStack 中的数据每次都是相同的effect往里面增加。 因为 obj.foo++ 其实也就是 obj.foo = obj.foo + 1 这就可以看出 这一行代码 既触发了收集依赖 又触发了执行 ,也就是同时触发 track trigger, 这就导致 track 同时将effect压入栈中, trigger 就去执行,所以每次执行都会有上一次的 effect

通过调试查看 effectStack 会越来越多,而且每次添加的都是相同的effect, 并且在执行的时候 const effectsToRun = new Set(effects); 每次都是添加相同的 effects,这就导致每次根据 effectsToRun 执行的时候都会有effect

解决问题:

那么在添加 effect 的时候,判断是否不与当前执行的effect 是否与 activeEffect 相同,如果不相等才会继续 push并执行。

javascript 复制代码
function trigger(target, key) {
  const depsMap = bucket.get(target);
  if (!depsMap) return;
  const effects = depsMap.get(key);
  debugger
  //   const effectsToRun = new Set(effects); // 新增
  //   effectsToRun.forEach((effectFn) => effectFn()); // 新增
  const effectsToRun = new Set();
  effects &&
    effects.forEach((effectFn) => {
      if (effectFn !== activeEffect) {
        effectsToRun.add(effectFn);
      }
    });
  effectsToRun.forEach((effectFn) => effectFn());
}

完整代码:

js 复制代码
const data = { foo: 1 };
let activeEffect;
const effectStack = [];

const bucket = new WeakMap();

export function effect(fn) {
  const effectFn = () => {
    cleanup(effectFn);
    activeEffect = effectFn;
    effectStack.push(effectFn);
    fn();

    effectStack.pop();
    activeEffect = effectStack[effectStack.length - 1];
  };
  effectFn.deps = [];
  effectFn();
}

export const obj = new Proxy(data, {
  get(target, key) {
    track(target, key);
    return target[key];
  },
  set(target, key, newVal) {
    target[key] = newVal;
    trigger(target, key);
    return true;
  },
});

function track(target, key) {
  if (!activeEffect) return target[key];
  let depsMap = bucket.get(target);
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()));
  }
  let deps = depsMap.get(key);
  if (!deps) {
    depsMap.set(key, (deps = new Set()));
  }
  deps.add(activeEffect);
  activeEffect.deps.push(deps);
}

function trigger(target, key) {
  const depsMap = bucket.get(target);
  if (!depsMap) return;
  const effects = depsMap.get(key);
  
  // 解决栈溢出问题
  const effectsToRun = new Set(effects); // 新增
  effectsToRun.forEach((effectFn) => effectFn()); // 新增
  //   const effectsToRun = new Set();
  //   effects &&
  //     effects.forEach((effectFn) => {
  //       if (effectFn !== activeEffect) {
  //         effectsToRun.add(effectFn);
  //       }
  //     });
  //   effectsToRun.forEach((effectFn) => effectFn());
}

function cleanup(fn) {
  fn && fn.deps.forEach((dep) => dep.delete(fn));
  fn.deps.length = 0;
}
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
烛阴9 分钟前
Express入门必学三件套:路由、中间件、模板引擎全解析
javascript·后端·express
哟哟耶耶17 分钟前
React-01React创建第一个项目(npm install -g create-react-app)
前端·javascript·react.js
张拭心20 分钟前
工作九年程序员的三月小结
android·前端
try again!30 分钟前
HTML快速上手
前端·css·html
Dontla31 分钟前
前端页面鼠标移动监控(鼠标运动、鼠标监控)鼠标防抖处理、mousemove、debounce()、事件停止触发、超时触发
前端·计算机外设
喝拿铁写前端44 分钟前
字段混乱如何影响系统治理?
前端
知远同学1 小时前
关闭Chrome提示更新失败的弹窗
前端·chrome
Heidi__2 小时前
Vue 3 的响应式原理
前端·javascript·vue.js
LinXunFeng2 小时前
Flutter - Xcode16 还原编译速度
前端·flutter·xcode
夏之小星星2 小时前
element-ui自制树形穿梭框
前端·javascript·ui·elementui·vue