通過前段時間的準備,筆者最近已經成功入職shopee。因此最近沒有更新內容,以後穩定下來以後會繼續進行輸出node
入職shopee以後,導師有給到一個entry task,這個任務是實現一個事件機制。實現addEventListener
,removeEventListener
,dispatchEvent
這三個方法。api
要求是這個樣子的:數組
兼容 W3C 的事件冒泡和事件捕獲模型(addEventListener 的 useCapture 參數)緩存
每一個事件將會有一個優先級(由開發者設置,最高爲 0,數字越大則優先級越低,若未設置則默認爲 0),若同一時刻有多個事件須要被執行,則按照優先級從高到低執行;若其中多個事件優先級相同,先被定義的事件先執行微信
在優先級與事件冒泡/捕獲模型衝突時,優先保證事件冒泡/捕獲的執行順序markdown
須要將你的代碼寫成一個 TypeScript 模塊,引入方式和 API 請參考文檔數據結構
若屢次爲同一元素綁定同一類型的同一 Listener,該事件在符合條件時只觸發一次,事件觸發優先級以最終註冊的優先級爲準。dom
加分項有個比較有意思的東西:儘量保證一幀的時間(16ms)中全部事件的執行時間之和不超過 10ms(暫時無需考慮超過 10ms 的單個事件),須要把在這一幀來不及執行的事件放到下一幀執行(依舊須要按照優先級來執行)
函數
W3C的事件模型是先捕獲後冒泡學習
入參爲dom節點,監聽方法名,回調方法,其餘配置能夠用...opt接受。
實現思路
:
將註冊的事件同一處理,建立weakmap對象,數據結構以下:
{
Dom:{
handleName(監聽方法名):{
// 存放處理捕獲和冒泡的數組
bubble:[{ // 冒泡數組
cb:()=>void // 事件觸發回調
range:0 // 此事件優先級
once:false // 兼容opt的once參數
}],
capture:[] // 捕獲數組,同上
}
}
}
複製代碼
方法內部還須要對原來的dom節點進行一次監聽,用來在用戶手動點擊觸發時的事件。這時候須要將此事件作一個緩存,以便在removeEventListener
的時候去取消監聽
入參爲dom節點,監聽方法名,回調,是否採用捕獲模型(可選,默認false)
實現思路:
首先是健壯性處理,而後對weakmap對象中對應節點的對應事件作刪除處理。而後把在addEventListener
的時候添加的監聽進行remove。
這個方法應該算是關鍵,他的入參是dom節點和監聽方法名。
實現思路:
首先進行健壯性處理,而後遞歸的將當前節點,父節點的捕獲和冒泡數組存入數組。
依次對數組中的回調進行調用。
10ms的實現,使用的api是requestAnimationFrame,rAF傳入一個回調,回調中能夠拿到一個time參數,time參數指示當前被 requestAnimationFrame() 排序的回調函數被觸發的時間。在同一個幀中的多個回調函數,它們每個都會接受到一個相同的時間戳,即便在計算上一個回調函數的工做負載期間已經消耗了一些時間。該時間戳是一個十進制數,單位毫秒,最小精度爲1ms(1000μs) (--來自MDN)。 再將performance.now()運行獲得的時間戳和當前的rAF回調接受到的time進行對比,若是在10ms內能夠繼續從數組中取事件進行調用。反之,則放入下一幀
once 的實現,若是有once參數,就對當前的對象引用置爲空對象
這裏能夠提供一下個人思路,你們也能夠本身嘗試寫一下。
// 遞歸的去將當前節點和父節點存入數組
function recurrenceFindNodeList(
caps: callbackType[],
bubs: callbackType[],
node: nodeType,
handleName: string
) {
const parent: any = node.parentNode;
if (eventMap.has(parent)) {
const parentObj = eventMap.get(parent)[handleName];
caps = [...parentObj.capture, ...caps];
bubs = [...bubs, ...parentObj.bubble];
recurrenceFindNodeList(caps, bubs, parent, handleName);
}
return [...caps, ...bubs];
}
// 對於10ms 的實現
requestAnimationFrame(handler);
function handler(time: number) {
let taskFinishTime: number = window.performance.now();
while (taskFinishTime - time < 10) {
const nextTask = tasklist.shift();
if (nextTask?.cb) {
nextTask.cb();
}
taskFinishTime = window.performance.now();
}
if (tasklist.length > 0) {
requestAnimationFrame(handler);
}
}
複製代碼
shopee是一個很是年輕化的公司,在這裏從技術角度說,能夠學習到不少新技術,並參加他們的項目從0到1的過程,相信在這裏的進步會很大。若是你們想了解蝦皮歡迎加我微信:zhi794855679 。
願不負努力,所願皆所求。