vue3調(diào)度器scheduler功能和用法詳解
調(diào)度器是vue3響應(yīng)式系統(tǒng)中一個非常重要的特性,可調(diào)度性指的是當(dāng)trigger 動作觸發(fā)副作用函數(shù)重新執(zhí)行時,有能力決定副作用函數(shù)執(zhí)行的時機、次數(shù)以及方式
const data = { foo: 1 }
const obj = new Proxy(data, { /* ... */ }) // 上文中的響應(yīng)式
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)度器對象掛在到當(dāng)前副作用函數(shù)中
// effect 函數(shù)用于注冊副作用函數(shù)
function effect(fn, options = {}) {
fn.options = options; // 新增掛在調(diào)度器
activeEffect = fn;
// 執(zhí)行副作用函數(shù)
fn();
}- 修改參數(shù)時,判斷是否存在調(diào)度器,存在,執(zhí)行當(dāng)前掛載中調(diào)度器方法
(在taigger函數(shù)中)
function trigger(target, key) {
// 根據(jù)target從桶中取得depsMap,它是key --> effects
const depsMap = bucket.get(target);
if (!depsMap) return;
// 根據(jù)key取得當(dāng)前對應(yīng)的副作用函數(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開啟一個宏任務(wù)來執(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ù)放在宏任務(wù)隊列中執(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:當(dāng)前對象,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上是否存在對應(yīng)的副作用函數(shù)
let deps = depsMap.get(key);
// 不存在,則新建一個new Set,并與key關(guān)聯(lián)
if (!deps) {
depsMap.set(key, (deps = new Set()));
}
// 最后將當(dāng)前激活的副作用函數(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取得當(dāng)前對應(yīng)的副作用函數(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ù)放在宏任務(wù)隊列中執(zhí)行
setTimeout(fn);
},
}
);
obj.foo++;
console.log("結(jié)束了");除了控制副作用函數(shù)的執(zhí)行順序,通過調(diào)度器還可以做到控制它的執(zhí)行次數(shù)
- 正常的打印結(jié)果應(yīng)該是
1,2,3,但是我們只關(guān)心執(zhí)行的最后結(jié)果,應(yīng)該拿到的是1,3,執(zhí)行三次有些多余,這時,就需要使用到調(diào)度器來修改執(zhí)行次數(shù)
const data = { foo: 1 }
const obj = new Proxy(data, { /* ... */ }) // 上文中的響應(yīng)式
effect(() => {
console.log(obj.foo)
})
obj.foo++
obj.foo++;- 實現(xiàn)不包含過渡階段,使用調(diào)度器基于
promise,可以直接修改當(dāng)前當(dāng)前調(diào)度函數(shù)
// 定義一個任務(wù)隊列,使用它自動去重能力
const jobQueue = new Set();
// 使用 Promise.resolve() 創(chuàng)建一個 promise 實例,我們用它將一個任務(wù)添加到微任務(wù)隊列
const p = Promise.resolve();
// 一個標(biāo)志代表是否正在刷新隊列
let isFlushing = false;
function flushJob() {
// 如果隊列正在刷新,則什么都不做
if (isFlushing) return;
// 設(shè)置為 true,代表正在刷新
isFlushing = true;
// 在微任務(wù)隊列中刷新 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自適應(yīng)高度問題
在寫公司后臺項目的時候遇到一個需求,要求表格頁面不能有滾動條,所以必須封裝一個公共方法來實現(xiàn)表格自適應(yīng)高度,本問小編給大家介紹了如何解決vue2 element el-table自適應(yīng)高度問題,需要的朋友可以參考下2023-11-11

