vue3調(diào)度器scheduler功能和用法詳解
調(diào)度器
是vue3響應式系統(tǒng)中一個非常重要的特性,可調(diào)度性指的是當trigger
動作觸發(fā)副作用函數(shù)重新執(zhí)行時,有能力決定副作用函數(shù)執(zhí)行的時機、次數(shù)以及方式
const data = { foo: 1 } const obj = new Proxy(data, { /* ... */ }) // 上文中的響應式 effect(() => { console.log(obj.foo) }) obj.foo++ console.log('結(jié)束了')
正常執(zhí)行結(jié)果順序是1,2,結(jié)束了
,但是,若我們期望的打印順序發(fā)生改變1,結(jié)束了,2
,要實現(xiàn)這樣的打印結(jié)果,就需要使用到調(diào)度器
- 可以為函數(shù)effect函數(shù)設(shè)計一個選項參數(shù)
options
,允許用戶指定調(diào)度器:
effect( () => { console.log(obj.foo); }, // options { // 調(diào)度器 scheduler 是一個函數(shù) scheduler(fn) { // ... }, });
- 將調(diào)度器對象掛在到當前副作用函數(shù)中
// effect 函數(shù)用于注冊副作用函數(shù) function effect(fn, options = {}) { fn.options = options; // 新增掛在調(diào)度器 activeEffect = fn; // 執(zhí)行副作用函數(shù) fn(); }
- 修改參數(shù)時,判斷是否存在調(diào)度器,存在,執(zhí)行當前掛載中調(diào)度器方法
(在taigger函數(shù)中)
function trigger(target, key) { // 根據(jù)target從桶中取得depsMap,它是key --> effects const depsMap = bucket.get(target); if (!depsMap) return; // 根據(jù)key取得當前對應的副作用函數(shù) const effects = depsMap.get(key); // 執(zhí)行副作用函數(shù) effects && effects.forEach((fn) => { // fn() if (fn.options.scheduler) { // 新增 fn.options.scheduler(fn); } else { // 否則直接執(zhí)行副作用函數(shù)(之前的默認行為) fn(); } }); }
- 使用
setTimeout
開啟一個宏任務來執(zhí)行副作用函數(shù) fn,這樣,就能改變執(zhí)行順序,拿到我們想要的執(zhí)行結(jié)果
function effect(fn, options = {}) { fn.options = options; // 新增 activeEffect = fn; // 執(zhí)行副作用函數(shù) fn(); } effect( () => { console.log(obj.foo); }, // options { // 調(diào)度器 scheduler 是一個函數(shù) scheduler(fn) { // 修改參數(shù),將副作用函數(shù)放在宏任務隊列中執(zhí)行 setTimeout(fn) }, } );
完整代碼:
const data = { foo: 1 }; // 用一個全局變量存儲被注冊的副作用函數(shù) let activeEffect; // 創(chuàng)建一個新桶來存儲副作用函數(shù),包含key和value const bucket = new WeakMap(); const obj = new Proxy(data, { get(target, key) { // target:當前對象,key:觸發(fā)監(jiān)聽的key track(target, key); return target[key]; }, set(target, key, newVal) { target[key] = newVal; trigger(target, key); }, }); // track函數(shù) function track(target, key) { // 沒有正在執(zhí)行的副作用函數(shù) 直接返回 if (!activeEffect) return target[key]; // 從這個桶中取出一個Map類型(key -> value) let depsMap = bucket.get(target); // 不存在,則創(chuàng)建一個Map與target關(guān)聯(lián) if (!depsMap) { bucket.set(target, (depsMap = new Map())); } // 根據(jù)key判斷每個key上是否存在對應的副作用函數(shù) let deps = depsMap.get(key); // 不存在,則新建一個new Set,并與key關(guān)聯(lián) if (!deps) { depsMap.set(key, (deps = new Set())); } // 最后將當前激活的副作用函數(shù)添加到桶中 deps.add(activeEffect); } // trigger函數(shù) function trigger(target, key) { // 根據(jù)target從桶中取得depsMap,它是key --> effects const depsMap = bucket.get(target); if (!depsMap) return; // 根據(jù)key取得當前對應的副作用函數(shù) const effects = depsMap.get(key); // 執(zhí)行副作用函數(shù) effects && effects.forEach((fn) => { if (fn.options.scheduler) { fn.options.scheduler(fn); } else { // 否則直接執(zhí)行副作用函數(shù)(之前的默認行為) fn(); } }); } // effect 函數(shù)用于注冊副作用函數(shù) function effect(fn, options = {}) { fn.options = options; // 新增 activeEffect = fn; // 執(zhí)行副作用函數(shù) fn(); } effect( () => { console.log(obj.foo); }, // options { // 調(diào)度器 scheduler 是一個函數(shù) scheduler(fn) { // 修改參數(shù),將副作用函數(shù)放在宏任務隊列中執(zhí)行 setTimeout(fn); }, } ); obj.foo++; console.log("結(jié)束了");
除了控制副作用函數(shù)的執(zhí)行順序,通過調(diào)度器
還可以做到控制它的執(zhí)行次數(shù)
- 正常的打印結(jié)果應該是
1,2,3
,但是我們只關(guān)心執(zhí)行的最后結(jié)果,應該拿到的是1,3
,執(zhí)行三次有些多余,這時,就需要使用到調(diào)度器
來修改執(zhí)行次數(shù)
const data = { foo: 1 } const obj = new Proxy(data, { /* ... */ }) // 上文中的響應式 effect(() => { console.log(obj.foo) }) obj.foo++ obj.foo++;
- 實現(xiàn)不包含過渡階段,使用調(diào)度器基于
promise
,可以直接修改當前當前調(diào)度函數(shù)
// 定義一個任務隊列,使用它自動去重能力 const jobQueue = new Set(); // 使用 Promise.resolve() 創(chuàng)建一個 promise 實例,我們用它將一個任務添加到微任務隊列 const p = Promise.resolve(); // 一個標志代表是否正在刷新隊列 let isFlushing = false; function flushJob() { // 如果隊列正在刷新,則什么都不做 if (isFlushing) return; // 設(shè)置為 true,代表正在刷新 isFlushing = true; // 在微任務隊列中刷新 jobQueue 隊列 p.then(() => { jobQueue.forEach((job) => job()); }).finally(() => { // 結(jié)束后重置 isFlushing isFlushing = false; }); }
- 執(zhí)行調(diào)度函數(shù)
effect( () => { console.log(obj.foo); }, // options { // 調(diào)度器 scheduler 是一個函數(shù) scheduler(fn) { jobQueue.add(fn); // 調(diào)用 flushJob 刷新隊列 flushJob(); }, } ); obj.foo++; obj.foo++;
到此這篇關(guān)于vue3調(diào)度器scheduler功能和用法詳解的文章就介紹到這了,更多相關(guān)vue3調(diào)度器scheduler內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3+vite+tdesign實現(xiàn)日歷式可編輯的排課班表填寫功能
本文介紹了如何使用Vue3和tdesign實現(xiàn)一個日歷式、可編輯的排班填寫功能,開發(fā)過程中面臨了年份和月份下拉框的實現(xiàn)、周期顯示以及可編輯日歷的樣式和數(shù)據(jù)獲取等挑戰(zhàn),感興趣的朋友一起看看吧2025-01-01一文解決vue2 element el-table自適應高度問題
在寫公司后臺項目的時候遇到一個需求,要求表格頁面不能有滾動條,所以必須封裝一個公共方法來實現(xiàn)表格自適應高度,本問小編給大家介紹了如何解決vue2 element el-table自適應高度問題,需要的朋友可以參考下2023-11-11