Vue 源碼思想在工作中的應(yīng)用
一、背景
由于電腦CPU、內(nèi)存等的限制,能夠同時(shí)啟動(dòng)的任務(wù)數(shù)有一定限制,例如一臺(tái)電腦能夠執(zhí)行5個(gè)異步任務(wù),但是目前有100個(gè)異步任務(wù)要執(zhí)行,那么如何讓這100個(gè)任務(wù)無(wú)間隔的快速執(zhí)行完畢呢?
二、問(wèn)題解答
剛遇到這個(gè)問(wèn)題的時(shí)候,也是出于懵逼狀態(tài),怎么處理呢???碰巧的是近期學(xué)習(xí)了一些Vue的源碼知識(shí),那么是不是可以借鑒其思想來(lái)解決遇到的這個(gè)難題呢?經(jīng)過(guò)一步步分析,確定答案是肯定的。下面從解題思路、知識(shí)點(diǎn)及代碼實(shí)現(xiàn)來(lái)聊一聊實(shí)現(xiàn)過(guò)程。
2.1 解題思路
上述是整個(gè)流程圖,其流程可簡(jiǎn)化為以下幾個(gè)步驟:
將所有任務(wù)分為兩組,任務(wù)組1指的是電腦可以并行執(zhí)行的異步任務(wù),任務(wù)組2指的是其余的異步任務(wù);
將任務(wù)組1變?yōu)榭蓭?tīng)狀態(tài),即其發(fā)生變化時(shí)我們能夠知道;
將任務(wù)組1中的任務(wù)觸發(fā)(不觸發(fā)任務(wù)不會(huì)執(zhí)行);
某一任務(wù)執(zhí)行完畢后,將任務(wù)組2中的一個(gè)任務(wù)填充到可幀聽(tīng)狀態(tài)的任務(wù)組1中;
任務(wù)按照固定數(shù)量不斷執(zhí)行,直到所有任務(wù)執(zhí)行完畢。
2.2 知識(shí)點(diǎn)
在Vue源碼中,Vue2.x使用Object.defineProperty()實(shí)現(xiàn)對(duì)數(shù)據(jù)的幀聽(tīng);Vue3.0使用Proxy實(shí)現(xiàn)對(duì)數(shù)據(jù)的幀聽(tīng)。本著趕時(shí)髦和Proxy確實(shí)優(yōu)秀的態(tài)度,在實(shí)現(xiàn)過(guò)程中也應(yīng)用了Proxy。Proxy作為一個(gè)新的知識(shí)點(diǎn),先了解一下其定義及使用方法。
2.2.1 定義
Proxy中文意思是“代理”,是在目標(biāo)對(duì)象之間架設(shè)一層“攔截”,從而可以修改某些操作的默認(rèn)行為。Proxy共支持十三種攔截操作:get、set、has、deleteProperty、ownKeys、getOwnPropertyDescriptor、defineProperty、preventExtensions、getPrototypeOf、isExtensible、setPrototypeOf、apply、construct。
2.2.2 簡(jiǎn)單使用
- function testProxy(obj) {
- return new Proxy(obj, {
- get: (target, key) => {
- console.log(`我被get攔截器攔截了,攔截的屬性是${key}`);
- },
- set: (target, key, value) => {
- console.log(`我被set攔截器攔截了,攔截的屬性是${key}, 新值是${value}`);
- target[key] = value;
- }
- });
- }
- const testObj = {
- a: 10
- };
- const proxy = testProxy(testObj);
- proxy.a = 100;
2.2.3 詳細(xì)使用
詳細(xì)用法可以參考阮一峰大佬的“ECMAScript 6入門”。
2.3 代碼實(shí)現(xiàn)
2.3.1 定義兩個(gè)任務(wù)隊(duì)列
首先定義兩個(gè)任務(wù)隊(duì)列,task1為開(kāi)始執(zhí)行的一批任務(wù),task2中為后續(xù)添加進(jìn)去的任務(wù)。
- const task1 = [1, 2, 3];
- const task2 = [4, 5, 6, 7, 8];
2.3.2 數(shù)據(jù)變?yōu)榭蓭?tīng)的函數(shù)
利用Proxy將數(shù)據(jù)變?yōu)榭蓭?tīng)狀態(tài)
- /**
- * 監(jiān)聽(tīng)模塊,監(jiān)聽(tīng)對(duì)應(yīng)數(shù)組的變化,保證其始終有一定長(zhǎng)度的內(nèi)容在運(yùn)行
- *
- * @param {Array} initArr 定長(zhǎng)任務(wù)的數(shù)組
- * @param {Function} callback 對(duì)應(yīng)的回調(diào)函數(shù)
- */
- function watcher(initArr, callback) {
- const proxy = new Proxy(initArr, {
- set(target, key, value, receiver) {
- target[key] = value;
- callback(value, key, receiver);
- }
- });
- return proxy;
- }
2.3.3 異步任務(wù)邏輯
- /**
- * 異步任務(wù)的運(yùn)行邏輯
- *
- * @param {number} taskIndex 異步任務(wù)的序號(hào)
- * @param {number} index 當(dāng)前任務(wù)在定長(zhǎng)任務(wù)的序號(hào)
- * @param {Proxy} proxy Proxy實(shí)例
- */
- function asyncTask(taskIndex, index, proxy) {
- console.log(`${index}索引處的任務(wù)${taskIndex}開(kāi)始執(zhí)行`);
- return new Promise(resolve => {
- setTimeout(() => {
- console.log(`${index}索引處的任務(wù)${taskIndex}執(zhí)行完畢`);
- // 當(dāng)任務(wù)隊(duì)列2中還有任務(wù)時(shí),進(jìn)入隊(duì)列替換任務(wù)1中執(zhí)行完的任務(wù)
- if (task2.length > 0) {
- proxy[index] = task2.shift();
- }
- }, 1000 + 2000 * Math.random());
- });
- }
2.3.4 主函數(shù)
- const proxy = watcher(task1, asyncTask);
- task1.forEach((taskIndex, index) => asyncTask(taskIndex, index, proxy));
- }
2.3.5 執(zhí)行結(jié)果
通過(guò)結(jié)果可以看到,當(dāng)一個(gè)任務(wù)完成時(shí)立刻將有一個(gè)新的任務(wù)進(jìn)入。
- 0索引處的任務(wù)1開(kāi)始執(zhí)行
- 1索引處的任務(wù)2開(kāi)始執(zhí)行
- 2索引處的任務(wù)3開(kāi)始執(zhí)行
- 0索引處的任務(wù)1執(zhí)行完畢
- 0索引處的任務(wù)4開(kāi)始執(zhí)行
- 2索引處的任務(wù)3執(zhí)行完畢
- 2索引處的任務(wù)5開(kāi)始執(zhí)行
- 1索引處的任務(wù)2執(zhí)行完畢
- 1索引處的任務(wù)6開(kāi)始執(zhí)行
- 0索引處的任務(wù)4執(zhí)行完畢
- 0索引處的任務(wù)7開(kāi)始執(zhí)行
- 2索引處的任務(wù)5執(zhí)行完畢
- 2索引處的任務(wù)8開(kāi)始執(zhí)行
- 1索引處的任務(wù)6執(zhí)行完畢
- 0索引處的任務(wù)7執(zhí)行完畢
- 2索引處的任務(wù)8執(zhí)行完畢
三、討論
針對(duì)此類問(wèn)題的處理方式,各位大佬有新的思路與方法歡迎留言,我們一起討論,共同進(jìn)步。
本文轉(zhuǎn)載自微信公眾號(hào)「前端點(diǎn)線面」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系前端點(diǎn)線面公眾號(hào)。