最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue開發(fā)設(shè)計分支切換與cleanup實例詳解

 更新時間:2022年11月28日 10:57:27   作者:凱心  
這篇文章主要為大家介紹了vue開發(fā)設(shè)計分支切換與cleanup實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

分支切換與cleanup

了解分支切換

代碼示例如下

const data = { ok: true, text: "hello world" };
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key);
      return target[key];
    },
    // 在set操作中,賦值,然后調(diào)用effect函數(shù)
    set(target, key, value) {
      target[key] = value;
      trigger(target, key);
      return true;
    },
  });
}
const obj = reactive(data);
effect(function effectFn(){
  document.body.innerText = obj.ok ? obj.text : "not";
});

當代碼字段obj.ok發(fā)生變化時,代碼執(zhí)行的分支會跟著變化,這就是分支切換。

分支切換可能會產(chǎn)生遺留的副作用函數(shù)。

上面代碼中有個三元運算式,如果obj.ok = true,則展示obj.text,此時,effectFn執(zhí)行會觸發(fā)obj.okobj.text的讀取操作,否則展示"not"

此時的依賴收集如下圖展示:

const data = { ok: true, text: "hello world" };
const obj = reactive(data);
effect(function effectFn(){
  document.body.innerText = obj.ok ? obj.text : "not";
});

分支切換導致的問題

當發(fā)生obj.ok改變且為false時,此時obj.text對應(yīng)的依賴effectFn不會執(zhí)行,

但是obj.text發(fā)生改變時,對應(yīng)的effectFn卻會執(zhí)行,頁面的內(nèi)容會被修改掉。這是不期望發(fā)生的!

此時,是key為ok對應(yīng)的effectFn依舊有效,

key為text對應(yīng)的effectFn為無效,應(yīng)該清除掉,如下圖展示

如何清除掉副作用函數(shù)的無效關(guān)聯(lián)關(guān)系?

  • 每次副作用函數(shù)執(zhí)行前,可以先把它從所有與之關(guān)聯(lián)的依賴集合中刪除,然后清空依賴集合的收集,
  • 當副作用函數(shù)執(zhí)行,所有會重新建立關(guān)聯(lián)。(副作用函數(shù)中,會重新執(zhí)行響應(yīng)式數(shù)據(jù)的get操作,從而進行收集依賴)

步驟:

  • 副作用函數(shù)收集與自身關(guān)聯(lián)的依賴集合
  • effect注冊副作用函數(shù)中為effectFn增添一個屬性deps,用來存儲依賴集合,

    track函數(shù)中,進行依賴集合的收集

  • 將副作用函數(shù)從與之關(guān)聯(lián)的所有依賴集合中移除,
  • effect注冊副作用函數(shù)中,觸發(fā)副作用函數(shù)前,清除副作用函數(shù)的依賴集合

疑問:為什么對傳入的副作用函數(shù)進行一層包裹?

  • 為了對副作用函數(shù)進行更多操作,
  • 為副作用函數(shù)增加deps屬性,作為收集依賴集合的容器
  • 清除副作用函數(shù)的依賴集合
function effect(fn) {
  const effectFn = () => {
    activeFn = effectFn;
    cleanup(effectFn);
    fn();
  };
  effectFn.deps = [];
  effectFn();
}
function cleanup(effectFn) {
  // 從副作用函數(shù)關(guān)聯(lián)的依賴集合中刪除副作用函數(shù),從而斷開關(guān)聯(lián)
  for (const deps of effectFn.deps) {
    deps.delete(effectFn);
  }
  // 重置effectFn.deps
  effectFn.deps.length = 0;
}
// 收集effectFn的依賴集合
function track(target, key) {
  if (!activeFn) return target[key];
  let depMap = bucket.get(target);
  if (!depMap) {
    depMap = new Map();
    bucket.set(target, depMap);
  }
  let deps = depMap.get(key);
  if (!deps) {
    deps = new Set();
    depMap.set(key, deps);
  }
  deps.add(activeFn);
  // 收集effectFn的依賴集合
  activeFn.deps.push(deps);
}

完整代碼

// 響應(yīng)式數(shù)據(jù)的基本實現(xiàn)
let activeFn = undefined;
const bucket = new WeakMap();
let times = 0;
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      console.log(target, key);
      if (times > 10) {
        throw "超出";
      }
      times++;
      console.log(times);
      track(target, key);
      return target[key];
    },
    // 在set操作中,賦值,然后調(diào)用effect函數(shù)
    set(target, key, value) {
      target[key] = value;
      trigger(target, key);
      return true;
    },
  });
}
// 收集effectFn的依賴集合
function track(target, key) {
  console.log("track");
  if (!activeFn) return target[key];
  let depMap = bucket.get(target);
  if (!depMap) {
    depMap = new Map();
    bucket.set(target, depMap);
  }
  let deps = depMap.get(key);
  if (!deps) {
    deps = new Set();
    depMap.set(key, deps);
  }
  deps.add(activeFn);
  // 收集effectFn的依賴集合
  activeFn.deps.push(deps);
}
function trigger(target, key) {
  const depMap = bucket.get(target);
  if (!depMap) return;
  const effects = depMap.get(key);
  if (!effects) return;
  effects.forEach((fn) => {
    fn();
  });
}
const data = { ok: true, text: "hello world" };
const obj = reactive(data);
function effect(fn) {
  const effectFn = () => {
    activeFn = effectFn;
    cleanup(effectFn);
    fn();
  };
  effectFn.deps = [];
  effectFn();
}
function cleanup(effectFn) {
  // 從副作用函數(shù)關(guān)聯(lián)的依賴集合中刪除副作用函數(shù),從而斷開關(guān)聯(lián)
  for (const deps of effectFn.deps) {
    deps.delete(effectFn);
  }
  // 重置effectFn.deps
  effectFn.deps.length = 0;
}
function effect0() {
  console.log("%cindex.js line:83 obj.text", "color: #007acc;", obj.text);
}
effect(effect0);
obj.text = "hello vue";

產(chǎn)生的問題:代碼運行發(fā)生棧溢出

具體問題代碼:

obj.text = "hello vue";
// 觸發(fā)trigger函數(shù)
function trigger(target, key) {
  ...
  // 調(diào)用包裝的副作用函數(shù)
  effects.forEach((fn) => { // 1.effects
    fn();
  });
}
// 上面的fn
const effectFn = () => {
  activeFn = effectFn;
  // 把副作用函數(shù)從依賴集合中刪除
  cleanup(effectFn);
  // 執(zhí)行副作用函數(shù),重新收集依賴
  fn();
};
function cleanup(effectFn) {
  // 從副作用函數(shù)關(guān)聯(lián)的依賴集合中刪除副作用函數(shù),從而斷開關(guān)聯(lián)
  for (const deps of effectFn.deps) { // 此處的deps是上面的 1.effects
    // deps刪除effectFn
    // effects中的副作用函數(shù)減少
    deps.delete(effectFn);
  }
  // 重置effectFn.deps
  effectFn.deps.length = 0;
}
function track(target, key) {
	...
  // 此處的deps是上面的 1.effects
  // effects添加副作用函數(shù)
  deps.add(activeFn);
  // 收集effectFn的依賴集合
  activeFn.deps.push(deps);
}
  • 當設(shè)置響應(yīng)式對象的值時,觸發(fā)trigger函數(shù),遍歷依賴集合,
  • 遍歷的過程中,每個回合,被包裹的副作用函數(shù)執(zhí)行,
  • cleanup,把副作用函數(shù)從依賴集合中刪除

    觸發(fā)副作用函數(shù)

    副作用函數(shù)執(zhí)行觸發(fā)響應(yīng)式數(shù)據(jù)的get操作,重新收集依賴函數(shù)

  • 繼續(xù)遍歷

所以: 在遍歷的過程中,每個回合刪除元素,增加元素,導致遍歷無法結(jié)束,導致棧溢出。

問題簡單用代碼展示如下:

const set = new Set([1])
set.forEach(item => {
  set.delete(1)
  set.add(1)
  console.log('遍歷中')
})

如何解決此種情況下的棧溢出?

將遍歷effects變成遍歷effects的拷貝的值,不修改到efftcts就可以了

function trigger(target, key) {
  const depMap = bucket.get(target);
  if (!depMap) return;
  const effects = depMap.get(key);
  if (!effects) return;
  const effectsToRun = new Set(effects)
  effectsToRun.forEach((fn) => {
    fn();
  });
}

嵌套的effect與effect棧

effect嵌套的場景?

在Vue中,Vue的渲染函數(shù)就是在一個effect中執(zhí)行的

主要的場景是:組件嵌套組件。

如果不支持effect嵌套,產(chǎn)生的后果

初始化

function effect(fn) {
  const effectFn = () => {
    activeFn = effectFn;
    activeFn.fnName = fn.name;
    console.log("fnName", activeFn.fnName);
    cleanup(effectFn);
    fn();
  };
  effectFn.deps = [];
  effectFn();
}
effect(function effect1() {
  console.log("effect1");
  effect(function effect2() {
    console.log("effect2", obj.text);
  });
  console.log("effect1", obj.ok);
});
// fnName effect1
// effect1
// fnName effect2
// effect2 hello world
// effect1 true

obj.ok = false;

// fnName effect2
// effect2 hello world

原因:

  • 執(zhí)行effect(effect1)代碼
  • 執(zhí)行effectFn
  • effectFn函數(shù)中,activeFn包裹的副作用函數(shù)為effect1
  • 執(zhí)行effect1
  • 觸發(fā)了effect(effect2),此時effect1還沒有被收集
  • 執(zhí)行effectFn
  • effectFn函數(shù)中,activeFn包裹的副作用函數(shù)為effect2
  • 執(zhí)行effect2
  • effect2被收集,effect2執(zhí)行完成
  • 繼續(xù)執(zhí)行effect1,此時activeFn包裹的副作用函數(shù)仍為effect2
  • 所以此時收集的副作用函數(shù)又為effect2
  • 執(zhí)行obj.ok = false;
  • 遍歷對應(yīng)的依賴集合,觸發(fā)effect2

支持嵌套

  • 需要把正在執(zhí)行,且沒有執(zhí)行完的被包裹的副作用函數(shù)存入棧中
  • 當最上面的被包裹的副作用函數(shù)執(zhí)行完,彈出
const effectStack = [];
function effect(fn) {
  const effectFn = () => {
    activeFn = effectFn;
    cleanup(effectFn);
    // 把當前執(zhí)行的函數(shù)壓入棧中
    effectStack.push(effectFn);
    fn();
    // 函數(shù)執(zhí)行完畢,彈出
    effectStack.pop();
    // activeFn賦值為還未執(zhí)行完的副作用函數(shù)
    activeFn = effectStack[effectStack.length - 1];
  };
  effectFn.deps = [];
  effectFn();
}

避免無限遞歸循環(huán)

產(chǎn)生無限遞歸循環(huán)的代碼:

const data = {foo : 1}
const obj = reactive(data)
effect(()=> obj.foo++)

原因分析:

() => {
  obj.foo = obj.foo + 1
}

obj.foo在讀取自身之后又設(shè)置自身

  • 讀取obj.foo會觸發(fā)track
  • track收集依賴后,然后繼續(xù)執(zhí)行上面的賦值操作
  • 設(shè)置obj.foo會觸發(fā)trigger
  • 然后遍歷依賴集合,再次觸發(fā)obj.foo的讀取
  • 循環(huán)

解決循環(huán)

  • 設(shè)置和讀取是在一個副作用函數(shù)中進行的,都是activeEffect
  • 如果trigger觸發(fā)執(zhí)行的副作用函數(shù)與當前正在執(zhí)行的副作用函數(shù)相同,則不觸發(fā)執(zhí)行
function trigger(target, key) {
  const depMap = bucket.get(target);
  if (!depMap) return;
  const effects = depMap.get(key);
  if (!effects) return;
  const effectsToRun = new Set();
  effects.forEach((fn) => {
    if (fn !== activeFn) {
      // 當觸發(fā)的fn與當前執(zhí)行的副作用函數(shù)不同時
      // 將fn添加到effectsToRun
      effectsToRun.add(fn);
    }
  });
  effectsToRun.forEach((fn) => {
    fn();
  });
}

完整代碼

// 響應(yīng)式數(shù)據(jù)的基本實現(xiàn)
let activeFn = undefined;
const bucket = new WeakMap();
// 副作用函數(shù)調(diào)用棧
const effectStack = [];
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key);
      return target[key];
    },
    // 在set操作中,賦值,然后調(diào)用effect函數(shù)
    set(target, key, value) {
      target[key] = value;
      trigger(target, key);
      return true;
    },
  });
}
// 收集effectFn的依賴集合
function track(target, key) {
  if (!activeFn) return target[key];
  let depMap = bucket.get(target);
  if (!depMap) {
    depMap = new Map();
    bucket.set(target, depMap);
  }
  let deps = depMap.get(key);
  if (!deps) {
    deps = new Set();
    depMap.set(key, deps);
  }
  deps.add(activeFn);
  // 收集effectFn的依賴集合
  activeFn.deps.push(deps);
}
function trigger(target, key) {
  const depMap = bucket.get(target);
  if (!depMap) return;
  const effects = depMap.get(key);
  if (!effects) return;
  const effectsToRun = new Set();
  effects.forEach((fn) => {
    if (fn !== activeFn) {
      // 當觸發(fā)的fn與當前執(zhí)行的副作用函數(shù)不同時
      // 將fn添加到effectsToRun
      effectsToRun.add(fn);
    }
  });
  effectsToRun.forEach((fn) => {
    if (fn.options.scheduler) {
      fn.options.scheduler(fn);
    } else {
      fn();
    }
  });
}
const data = { ok: true, text: "hello world" };
const obj = reactive(data);
function effect(fn) {
  const effectFn = () => {
    activeFn = effectFn;
    cleanup(effectFn);
    // 把當前執(zhí)行的函數(shù)壓入棧中
    effectStack.push(effectFn);
    fn();
    // 函數(shù)執(zhí)行完畢,彈出
    effectStack.pop();
    // activeFn賦值為還未執(zhí)行完的副作用函數(shù)
    activeFn = effectStack[effectStack.length - 1];
  };
  effectFn.deps = [];
  effectFn();
}
function cleanup(effectFn) {
  // 從副作用函數(shù)關(guān)聯(lián)的依賴集合中刪除副作用函數(shù),從而斷開關(guān)聯(lián)
  for (const deps of effectFn.deps) {
    deps.delete(effectFn);
  }
  // 重置effectFn.deps
  effectFn.deps.length = 0;
}
function effect0() {
  console.log("%cindex.js line:83 obj.text", "color: #007acc;", obj.text);
}
effect(effect0);
obj.text = "hello vue";

以上就是vue開發(fā)設(shè)計分支切換與cleanup實例詳解的詳細內(nèi)容,更多關(guān)于vue開發(fā)設(shè)計分支切換cleanup的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 在Vue?3中使用OpenLayers加載GPX數(shù)據(jù)并顯示圖形效果

    在Vue?3中使用OpenLayers加載GPX數(shù)據(jù)并顯示圖形效果

    本文介紹了如何在Vue 3中使用OpenLayers加載GPX格式的數(shù)據(jù)并在地圖上顯示圖形,通過使用OpenLayers的GPX解析器,我們能夠輕松地處理和展示來自GPS設(shè)備的地理數(shù)據(jù),需要的朋友可以參考下
    2024-12-12
  • vue插件vue-resource的使用筆記(小結(jié))

    vue插件vue-resource的使用筆記(小結(jié))

    本篇文章主要介紹了vue插件vue-resource的使用筆記(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • vue.js購物車添加商品組件的方法

    vue.js購物車添加商品組件的方法

    這篇文章主要介紹了vue.js購物車添加商品組件的方法,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • 使用Vue3封裝一個通用echarts組件詳解

    使用Vue3封裝一個通用echarts組件詳解

    這篇文章主要為大家詳細介紹了使用Vue3封裝一個通用echarts組件詳解的相關(guān)知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-02-02
  • vue使用input封裝上傳文件圖片全局組件的示例代碼

    vue使用input封裝上傳文件圖片全局組件的示例代碼

    實際開發(fā)過程中,我們經(jīng)常遇見需要上傳文件圖片功能,可以封裝一個全局組件來調(diào)用,這篇文章給大家介紹vue使用input封裝上傳文件圖片全局組件,感興趣的朋友跟隨小編一起看看吧
    2023-11-11
  • 解決vue腳手架項目打包后路由視圖不顯示的問題

    解決vue腳手架項目打包后路由視圖不顯示的問題

    今天小編就為大家分享一篇解決vue腳手架項目打包后路由視圖不顯示的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • Vue框架里使用Swiper的方法示例

    Vue框架里使用Swiper的方法示例

    這篇文章主要介紹了Vue框架里使用Swiper的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • vue中如何配置proxy代理

    vue中如何配置proxy代理

    這篇文章主要介紹了vue中如何配置proxy代理問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Vue3自定義打印實現(xiàn)原理詳解

    Vue3自定義打印實現(xiàn)原理詳解

    近接觸到了一個 Vue3 的打印需求,我發(fā)現(xiàn)自己雖然從事前端開發(fā)已有多年,但對如何實現(xiàn)自定義打印還沒有深入研究,一般都是找現(xiàn)成的庫來解決問題,借這次的機會研究了一下如何實現(xiàn)自定義打印,需要的朋友可以參考下
    2024-07-07
  • vue實現(xiàn)下拉菜單效果

    vue實現(xiàn)下拉菜單效果

    這篇文章主要為大家詳細介紹了vue實現(xiàn)下拉菜單效果,運用了hover顯示與隱藏以及定位,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09

最新評論

河津市| 贵南县| 清流县| 喀什市| 襄樊市| 逊克县| 朝阳区| 东乌珠穆沁旗| 民乐县| 抚顺县| 华阴市| 福州市| 富源县| 石家庄市| 潞西市| 缙云县| 苍南县| 吉安市| 吉首市| 临洮县| 五常市| 安国市| 陈巴尔虎旗| 彭州市| 武冈市| 华阴市| 常州市| 张家川| 北碚区| 海门市| 新泰市| 盐山县| 南江县| 康定县| 革吉县| 灌南县| 肥西县| 安远县| 黎平县| 辽宁省| 南京市|