寫一個簡單的支援Node.js&瀏覽器的自定義事件庫

?晨辰?發表於2019-02-20

原文地址

自定義事件庫 EventFire

原始碼

EventFire倉庫地址: github.com/ccforward/E… (求點進去加個Star)

事件的管理主要有三點:繫結(on)、觸發(fire)、銷燬(off);所以寫一個自定義的事件庫就從這三點出發。

下面一步一步來寫

0、思考

繫結

就像在各種js庫裡面監聽DOM事件一樣,會有下面幾種方式:

event.on(`someEvent`, callback)
// 繫結多個事件
event.on([`someEventA`, `someEventB`], callback)
// 繫結一次
event.once(`someEvent`, callback)
// ....  其他複製程式碼

觸發

PS: 觸發的函式名可以是 trigger 或者 emmit,個人感覺 fire 像遊戲一樣,聽起來更爽。

event.fire(`someEvent`)
// 觸發時繫結資料
event.fire(`someEvent`, {weapon: `machine gun`})
// 觸發多個事件
event.fire([`someEventA`, `someEventB`], callback)

// ....  其他複製程式碼

銷燬

銷燬肯定和事件繫結是對應關係

event.off(`someEvent`, callback)
event.off(`someEvent`)
event.off([`someEventA`, `someEventB`], callback)

// ....  其他複製程式碼

1、方法設計

一個簡單的事件庫應該有如下的方法:

  • on 事件繫結
  • once 繫結一次
  • off 事件解綁
  • fire 觸發事件
  • offAll 解綁所有事件
  • listeners 返回某一事件上的監聽器
  • enable 事件繫結和觸發-可用
    disable 事件繫結和觸發-暫停
  • destory 解綁例項上的事件,並完全銷燬這個例項(不能再繼續繫結和觸發事件)

2、方法細節

on 方法

最開始時已經有了兩個基本的用法,思考後想到一些新的傳參方式來支援更加靈活的事件繫結:

  • 字串引數,單個事件
    on(`event`, callback, {once: true})
  • 陣列引數,事件集合
    on([`event1`, `event2`], callback, {once: true})
  • 事件和回撥的鍵值對

    on({
      event1: function(){},
      event2: function(){}
    }, {once: true});複製程式碼
  • 繫結到所有事件上

    on(function(){}, {once: true})複製程式碼
  • 函式監聽器的名字也應該能支援正則

    on(/^event/)
    on(/eventd+/)複製程式碼

最後一個可選引數是考慮到 once 方法後來新增的,對於 on 方法直接單次的事件繫結會更靈活些

on 最後還應該返回 this 來支援鏈式呼叫

once 方法

on 方法上新增了 {once: true} 這個可選引數後,這個方法就僅僅是 on 方法的一個變形了,不再多說。

once 可以和最後提到的 scope 統一放在配置項中

off 方法

off 很好理解, 它設計肯定和 on 是對應的,不過會多一種呼叫方式:

off(`eventName`) 解綁 eventName 事件

fire 方法

fire 也是和 on 相對應的:

  • fire(`event`);
  • fire(`event`, data);
  • fire([`event1`, `event2`]);
  • fire([`event1`, `event2`], data);
  • fire({event1: data, event2: data});

引數 data 可以用在回撥函式中,用來傳遞狀態、自定義資料等訊息

offAll 方法

這裡需要建立三個內部變數,用來儲存回撥函式,從而在解綁的時候能夠找到已經繫結的函式

  • _handlers 儲存繫結的監聽器回撥函式的鍵值對
  • _handlersAll 儲存繫結的在所有事件物件上的監聽器陣列
    就是這種 on(function(){})
  • _handlersRegx 儲存通過正則方式繫結的監聽器以及正則pattern

所以解綁所有事件就是把上面三個變數置為空

PS: 這個方法也可直接用在構造器中,初始化上面三個內部變數

listeners 方法

listeners(eventName) 返回一個繫結在 eventName 上的所有函式的陣列

enable disabled 方法

這兩個方法最開始考慮叫 pausegoon 但只是個構思,後來看了其他的事件庫後發現暫停繫結事件的執行是個很大的需求才改為更通用的名字

同樣這裡也需要引入一個內部變數 _enabled 來對應兩個方法設定為 truefalse

destory 方法

這個方法實現起來最簡單粗暴,三步:

  1. offAll() 解綁
  2. 把所有內部變數設為 nullfalse
  3. 上面所有方法設為空函式,也就是 Function.prototype

3、其他

作用域

函式在繫結的時候可以新增一個作用域,類似新增 {once: true} 一樣, 新增一個名為 scope 的配置來替代 this

on(`event1`, fn1, {scope: {hello: `world`}})
on(`event2`, fn2, {scope: {hello: `world`}, once: true})複製程式碼

資料傳遞

fire 方法的最後一個引數 data, 但是在 fire 的時候需要傳遞資料,因此一個 data 變數就是個剛需了。

e.on(`event`, function(ev){
    console.log(ev.data)
});
e.fire(`event`, {a: 123});複製程式碼

相容性

最後新增 commonjs 和 AMD 規範的相容,具體的程式碼在 index.js 最後

相關文章