使用TypeScript實(shí)現(xiàn)一個(gè)類(lèi)型安全的EventBus示例詳解_第1頁(yè)
使用TypeScript實(shí)現(xiàn)一個(gè)類(lèi)型安全的EventBus示例詳解_第2頁(yè)
使用TypeScript實(shí)現(xiàn)一個(gè)類(lèi)型安全的EventBus示例詳解_第3頁(yè)
使用TypeScript實(shí)現(xiàn)一個(gè)類(lèi)型安全的EventBus示例詳解_第4頁(yè)
使用TypeScript實(shí)現(xiàn)一個(gè)類(lèi)型安全的EventBus示例詳解_第5頁(yè)
已閱讀5頁(yè),還剩9頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第使用TypeScript實(shí)現(xiàn)一個(gè)類(lèi)型安全的EventBus示例詳解目錄前言準(zhǔn)備工作目標(biāo)思路具體實(shí)現(xiàn)全部代碼后記

前言

隨著vue3的發(fā)布,TypeScript在國(guó)內(nèi)越來(lái)越流行,學(xué)習(xí)TypeScript也隨即變成了大勢(shì)所趨。本文就通過(guò)實(shí)現(xiàn)一個(gè)類(lèi)型安全的EventBus來(lái)練習(xí)TypeScript,希望對(duì)小伙伴們有所幫助。

準(zhǔn)備工作

生成一個(gè)TypeScript的基礎(chǔ)架子:

//創(chuàng)建目錄

mkdirts-event-buscdts-event-bus

//初始化工程

yarninit-y

//安裝typescript

yarnaddtypescript-D

//生成typescript配置文件

npxtsc--init

這樣一來(lái)我們就搭建好了一個(gè)TypeScript的基礎(chǔ)架子,為了方便我們后續(xù)的測(cè)試,我們需要下載ts-node,它可以讓我們?cè)诓痪幾gTypeScript代碼的情況下運(yùn)行TypeScript。

yarnaddts-node-D

目標(biāo)

基礎(chǔ)功能完備,包括注冊(cè),發(fā)布,取消訂閱三個(gè)核心功能。類(lèi)型安全,能約束我們輸入的參數(shù),并且有代碼提示。

思路

每一個(gè)Event都可以注冊(cè)多個(gè)處理函數(shù),我們用一個(gè)Set來(lái)保存這些處理函數(shù),再用一個(gè)Map來(lái)保存Event到對(duì)應(yīng)Set的映射,如圖所示:

具體實(shí)現(xiàn)

//定義泛型函數(shù)類(lèi)型

typeHandlerT=any=(val:T)=void;

classEventBusEventsextendsRecordstring,any{

/**保存key=set映射*/

privatemap:Mapstring,SetHandler=newMap();

onEventNameextendskeyofEvents(

name:EventName,

handler:HandlerEvents[EventName]

letset:SetHandlerEvents[EventName]|undefined=this.map.get(

nameasstring

if(!set){

set=newSet();

this.map.set(nameasstring,set);

set.add(handler);

}

這里我們分成邏輯和類(lèi)型兩方面來(lái)講

邏輯方面,我們初始化了一個(gè)空的Map,然后當(dāng)調(diào)用on用來(lái)注冊(cè)事件的時(shí)候,先去根據(jù)EventName來(lái)找有沒(méi)有對(duì)應(yīng)的Set,沒(méi)有就創(chuàng)建一個(gè),并且把事件添加到Set中,這一部分的代碼相當(dāng)簡(jiǎn)單,實(shí)現(xiàn)起來(lái)也沒(méi)什么難度。

類(lèi)型方面,我們將EventBus定義為一個(gè)泛型類(lèi),并約束泛型為EventsextendsRecordstring,any,這樣就約束了傳入的泛型參數(shù)必須是一個(gè)對(duì)象類(lèi)型,例如:

typeEvents={

foo:number;

bar:string;

}

我們可以通過(guò)這個(gè)類(lèi)型來(lái)獲取key對(duì)應(yīng)value的類(lèi)型

//number;

typeValueTypeOfFoo=Events['foo']

進(jìn)而可以獲取foo事件對(duì)應(yīng)的handler函數(shù)的類(lèi)型,即:

//(val:number)=void;

typeHandlerOfFoo=HandlerEvents['foo']

我們又將on方法設(shè)置為泛型函數(shù),同時(shí)約束EventNameextendskeyofEvents,這樣一來(lái)Events[EventName]就是對(duì)應(yīng)值的類(lèi)型,HandlerEvents[EventName]就是處理函數(shù)的類(lèi)型。通過(guò)這樣的方式我們實(shí)現(xiàn)了一個(gè)類(lèi)型安全的on方法。

接著我們編寫(xiě)一段代碼測(cè)試一下

可以看到,我們?cè)趘scode中編寫(xiě)代碼的時(shí)候,編輯器能給我們代碼提示了。

我們鍵入handler函數(shù),編輯器也會(huì)提醒我們val是一個(gè)string類(lèi)型。

當(dāng)我們傳的參數(shù)不合法的時(shí)候,TypeScript也會(huì)給我們警告

接下來(lái)我們依葫蘆畫(huà)瓢實(shí)現(xiàn)emit函數(shù)。

classEventBusEventsextendsRecordstring,any{

...otherscode

/**觸發(fā)事件*/

emitEventNameextendskeyofEvents(

name:EventName,

value:Events[EventName]

constset:SetHandlerEvents[EventName]|undefined=this.map.get(

nameasstring

if(!set)return;

constcopied=[...set];

copied.forEach((fn)=fn(value));

}

先找到EventName對(duì)應(yīng)的Set,如果有就取出并依次執(zhí)行。這里的邏輯也相當(dāng)簡(jiǎn)單,我們編寫(xiě)代碼測(cè)試一下

constbus=newEventBus{

foo:string;

bar:number;

bus.on("foo",(val)={

console.log(val);

//輸出hello

bus.emit("foo","hello");

我們?cè)诮K端運(yùn)行npxts-node./index.ts,輸出hello,說(shuō)明我們的程序已經(jīng)生效。

接下來(lái)我們實(shí)現(xiàn)取消訂閱的功能。

{

offEventNameextendskeyofEvents(

name:EventName,

handler:HandlerEvents[EventName]

):void{

//什么都不傳,則清除所有事件

if(!name){

this.map.clear();

return;

//只傳名字,則清除同名事件

if(!handler){

this.map.delete(nameasstring);

return;

//name和handler都傳了,則清除指定handler

consthandlers:SetHandlerEvents[EventName]|undefined=this.map.get(

nameasstring

if(!handlers){

return;

handlers.delete(handler);

}

取消訂閱我們這樣設(shè)計(jì),它傳入0至2個(gè)參數(shù),什么都不傳代表清除所有事件,只傳一個(gè)參數(shù)代表清除同名事件,傳兩個(gè)參數(shù)代表只清除該事件指定的處理函數(shù),所以它的兩個(gè)參數(shù)都是可選的,實(shí)現(xiàn)的邏輯也非常簡(jiǎn)單,我們這里不多贅述。

我們編寫(xiě)一段測(cè)試代碼看下效果

constbus=newEventBus{

foo:string;

bar:number;

//測(cè)試傳2個(gè)參數(shù)的情況

consthandlerFoo1=(val:string)={

console.log("2個(gè)參數(shù)handlerFoo1=",val);

bus.on("foo",handlerFoo1);

bus.emit("foo","hello");

//打印2個(gè)參數(shù)handlerFoo1=hello

bus.off("foo",handlerFoo1);

bus.emit("foo","hello");

//什么都沒(méi)打印

//測(cè)試傳1個(gè)參數(shù)的情況

consthandlerFoo2=(val:string)={

console.log("1個(gè)參數(shù)handlerFoo2=",val);

consthandlerFoo3=(val:string)={

console.log("1個(gè)參數(shù)handlerFoo3=",val);

bus.on("foo",handlerFoo2);

bus.on("foo",handlerFoo3);

bus.emit("foo","hello");

//打印1個(gè)參數(shù)handlerFoo2=hello

//打印1個(gè)參數(shù)handlerFoo3=hello

bus.off("foo");

bus.emit("foo","hello");

//什么都沒(méi)輸出

//測(cè)試傳0個(gè)參數(shù)的情況

consthandlerFoo4=(val:string)={

console.log("0個(gè)參數(shù)handlerFoo4=",val);

consthandlerBar1=(val:number)={

console.log("0個(gè)參數(shù)handlerBar1=",val);

bus.on("foo",handlerFoo4);

bus.on("bar",handlerBar1);

bus.emit("foo","hello");

bus.emit("bar",123);

//打印1個(gè)參數(shù)handlerFoo4=hello

//打印1個(gè)參數(shù)handlerBar1=123

bus.off();

bus.emit("foo","hello");

bus.emit("bar",123);

//什么都沒(méi)輸出

從測(cè)試結(jié)果來(lái)看,我們的off方法功能也沒(méi)問(wèn)題,這樣就完成了我們的EventBus。

此外,我們還可以給我們的方法加上注釋?zhuān)@樣在我們鼠標(biāo)移到api上方和我們輸入?yún)?shù)的時(shí)候,編輯器就會(huì)有提示。

/**

*訂閱事件

*@paramname事件名

*@paramhandler事件處理函數(shù)

onEventNameextendskeyofEvents(

name:EventName,

handler:HandlerEvents[EventName]

letset:SetHandlerEvents[EventName]|undefined=this.map.get(

nameasstring

if(!set){

set=newSet();

this.map.set(nameasstring,set);

set.add(handler);

}

可以看到,編輯器給我們提供了很好的提示,極大方便了我們的編碼。

我們還可以用函數(shù)重載來(lái)改進(jìn)我們的off方法,以獲得更友好的提示

{

*清除所有事件

off():void;

*清除同名事件

*@paramname事件名

offEventNameextendskeyofEvents(name:EventName):void;

*清除指定事件

*@paramname事件名

*@paramhandler事件處理函數(shù)

offEventNameextendskeyofEvents(

name:EventName,

handler:HandlerEvents[EventName]

):void;

offEventNameextendskeyofEvents(

name:EventName,

handler:HandlerEvents[EventName]

):void{

//什么都不傳,則清除所有事件

if(!name){

this.map.clear();

return;

//只傳名字,則清除同名事件

if(!handler){

this.map.delete(nameasstring);

return;

//name和handler都傳了,則清除指定handler

consthandlers:SetHandlerEvents[EventName]|undefined=this.map.get(

nameasstring

if(!handlers){

return;

handlers.delete(handler);

改造前的提示:

改造后的提示:

至此,我們就完成了一個(gè)功能完備,類(lèi)型安全的EventBus了。

全部代碼

typeHandlerT=any=(val:T)=void;

classEventBusEventsextendsRecordstring,any{

privatemap:Mapstring,SetHandler=newMap();

*訂閱事件

*@paramname事件名

*@paramhandler事件處理函數(shù)

onEventNameextendskeyofEvents(

name:EventName,

handler:HandlerEvents[EventName]

letset:SetHandlerEvents[EventName]|undefined=this.map.get(

nameasstring

if(!set){

set=newSet();

this.map.set(nameasstring,set);

set.add(handler);

*觸發(fā)事件

*@paramname事件名

*@paramhandler事件處理函數(shù)

emitEventNameextendskeyofEvents(

name:EventName,

value:Events[EventName]

constset:SetHandlerEvents[EventName]|undefined=this.map.get(

nameasstring

if(!set)return;

constcopied=[...set];

copied.forEach((fn)=fn(value));

*清除所有事件

off():void;

*清除同名事件

*@paramname事件名

offEventNameextendskeyofEvents(name:EventName):void;

*清除指定事件

*@paramname事件名

*@paramhandler處理函數(shù)

offEventNameextendskeyofEvents(

name:EventName,

handler:HandlerEvents[EventName]

):void;

offEventNameextendskeyofEvents(

name:EventName,

handler:HandlerEvents[EventName]

):void{

//什么都不傳,則清除所有事件

if(!name){

this.map.clear();

return;

//只傳名字,則清除同名事件

if(!handler){

this.map.delete(nameasstring);

return;

//name和handler都傳了,則清除指定handler

consthandlers:SetHandlerEvents[EventName]|undefined=this.map.get(

nameasstring

if(!handlers){

return;

handlers.delete(handler);

constbus=newEventBus{

foo:string;

bar:number;

//測(cè)試傳2個(gè)參數(shù)的情況

consthandlerFoo1=(val:string)={

console.log("2個(gè)參數(shù)handlerFoo1=",val);

bus.on("foo",handlerFoo1);

bus.emit("foo","hello");

//打印2個(gè)參數(shù)handlerFoo1=hello

bus.off("foo",handlerFoo1);

bus.emit("foo","hello");

//什么都沒(méi)打印

//測(cè)試傳1個(gè)參數(shù)的情況

consthandlerFoo2=(val:string)={

console.log("1個(gè)參數(shù)handlerFoo2=",val);

con

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論