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;
}
相关推荐
weixin_4277716123 分钟前
css font-size 的妙用
前端·css
凤凰战士芭比Q1 小时前
web中间件——Nginx
前端·nginx·中间件
一 乐2 小时前
点餐|智能点餐系统|基于java+ Springboot的动端的点餐系统小程序(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·小程序·论文
视图猿人2 小时前
RxJS基本使用及在next.js中使用的例子
开发语言·javascript
bitbitDown2 小时前
从零打造一个 Vite 脚手架工具:比你想象的简单多了
前端·javascript·面试
liangshanbo12153 小时前
CSS 数学函数完全指南:从基础计算到高级动画
前端·css
码上成长4 小时前
GraphQL:让前端自己决定要什么数据
前端·后端·graphql
冴羽4 小时前
为什么在 JavaScript 中 NaN !== NaN?背后藏着 40 年的技术故事
前端·javascript·node.js
久爱@勿忘4 小时前
vue下载项目内静态文件
前端·javascript·vue.js
前端炒粉4 小时前
21.搜索二维矩阵 II
前端·javascript·算法·矩阵