大家好,我是前端西瓜哥。
PixiJS 的 Runner 類是高性能的事件通知類。其實就是一個簡易的發布訂閱庫。
發布訂閱庫,我們比較熟悉的就是 Nodejs 的 EventEmitter。
不過這個 Runner 的邏輯稍微有點特殊,后面會說它怎么特殊。
import { Runner } from "@pixi/runner";const loadedRunner = new Runner("loaded");const listener = { loaded(n1: number, n2: number) { console.log("前端西瓜哥", n1, n2); }};loadedRunner.add(listener);loadedRunner.emit(1, 2); // 輸出:前端西瓜哥 1 2
首先通過 new Runner(name) 創建一個 Runner 實例,這里需要傳入一個字符串類型的 name。
之后通過 runner.add 方法添加一個監聽器對象 listener。
最后通過 runner.emit 方法觸發事件,之前綁定的監聽器的 listener[name] 方法會被執行。
和我們熟悉的 Nodejs 的 EventEmitter 不一樣,有一些特別的點:
然后它和 EventEmitter 一樣,是類型不安全的:emit 傳的參數并沒有限定。
首先是構造函數。
export class Runner { public items: any[]; private _name: string; private _aliasCount: number; constructor(name: string) { this.items = []; this._name = name; this._aliasCount = 0; } // ...}
簡單的初始化操作,這個 name 我們要保存下來,之后我們執行監聽器對象,需要這個 name 作為 key 去找到方法去執行。
items 是保存監聽器對象的數組。
_aliasCount 是一個標識,標識是否在 emit(觸發事件)階段,用于防止 emit 時改變了 items,導致不可預期的行為。
然后是 add 方法,用于添加監聽器。
public add(item: unknown): this { if ((item as any)[this._name]) { this.ensureNonAliasedItems(); this.remove(item); // 如果存在,先刪除 this.items.push(item); // 添加的末尾 } return this;}
監聽器對象必須有對應的 key 才能被添加進去。
為了保證 this.items 不出現多個相同的對象,會將其刪除。然后把監聽器對象放到 this.items 末尾。
返回 this,是為了實現鏈式調用。
this.ensureNonAliasedItems() 方法用于處理一些特殊 case。
比如在 emit 階段發生了 add 操作,PixiJS 會防止其在本輪 emit 被執行,為此會拷貝一份新的 items。
private ensureNonAliasedItems(): void { if (this._aliasCount > 0 && this.items.length > 1) { this._aliasCount = 0; this.items = this.items.slice(0); }}
emit 會觸發事件,別名有 dispatch、run。
public emit( a0?: unknown, a1?: unknown, a2?: unknown, a3?: unknown, a4?: unknown, a5?: unknown, a6?: unknown, a7?: unknown): this { if (arguments.length > 8) { throw new Error('max arguments reached'); } const { name, items } = this; this._aliasCount++; for (let i = 0, len = items.length; i < len; i++) { items[i][name](a0, a1, a2, a3, a4, a5, a6, a7); } if (items === this.items) { this._aliasCount--; } return this;}
核心邏輯:遍歷 this.items 數組,順序執行監聽器的 key 為 this.name 的方法。
remove,刪除監聽器。
public remove(item: unknown): this { const index = this.items.indexOf(item); if (index !== -1) { this.ensureNonAliasedItems(); this.items.splice(index, 1); } return this;}
contains:查看指定對象是已經是被綁定為監聽器。
public contains(item: unknown): boolean { return this.items.includes(item);}
removeAll:刪除所有監聽器.
public removeAll(): this { this.ensureNonAliasedItems(); this.items.length = 0; return this;}
destory:銷毀。銷毀后就不能再用了,否則會報錯。
public destroy(): void { this.removeAll(); this.items = null; this._name = null;}
empty,是一個 getter,判斷是否存在監聽器集合。PixiJS 確實喜歡用 getter
public get empty(): boolean { return this.items.length === 0;}
通常我們會在 PixiJS 的類中看到名為 disposeRunner 的成員屬性,說明這個類會通過事件訂閱的方式和其他模塊通信。
發布訂閱庫我實在是分析得夠多了,基本的套路就 3 個:把監聽器函數放到有序表中,觸發事件時順序調用,支持刪除監聽器(3 種風格)。
PixiJS 的 Runner 功能并不多,其中特殊的調用邏輯(調用監聽器的特定 key)顯然是用于 PixiJS 內部模塊的風格。
本文鏈接:http://www.tebozhan.com/showinfo-26-13254-0.htmlPixiJS 源碼解讀:Runner 事件通知類
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 如何更優雅的編程?面向接口編程四大法寶!