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;
}
相关推荐
多多*37 分钟前
Spring之Bean的初始化 Bean的生命周期 全站式解析
java·开发语言·前端·数据库·后端·spring·servlet
linweidong41 分钟前
在企业级应用中,你如何构建一个全面的前端测试策略,包括单元测试、集成测试、端到端测试
前端·selenium·单元测试·集成测试·前端面试·mocha·前端面经
满怀10151 小时前
【HTML 全栈进阶】从语义化到现代 Web 开发实战
前端·html
繁依Fanyi1 小时前
用 UniApp 构建习惯打卡 App —— HabitLoop 开发记
javascript·uni-app·codebuddy首席试玩官
东锋1.31 小时前
前端动画库 Anime.js 的V4 版本,兼容 Vue、React
前端·javascript·vue.js
满怀10151 小时前
【Flask全栈开发指南】从零构建企业级Web应用
前端·python·flask·后端开发·全栈开发
小杨升级打怪中2 小时前
前端面经-webpack篇--定义、配置、构建流程、 Loader、Tree Shaking、懒加载与预加载、代码分割、 Plugin 机制
前端·webpack·node.js
Yvonne爱编码2 小时前
CSS- 4.4 固定定位(fixed)& 咖啡售卖官网实例
前端·css·html·状态模式·hbuilder
SuperherRo3 小时前
Web开发-JavaEE应用&SpringBoot栈&SnakeYaml反序列化链&JAR&WAR&构建打包
前端·java-ee·jar·反序列化·war·snakeyaml
大帅不是我3 小时前
Python多进程编程执行任务
java·前端·python