1 回答

TA貢獻2037條經驗 獲得超6個贊
DOM事件
在Vue中為DOM元素綁定事件的具體方法在文章中的 方法與事件處理器 章節,通過v-on指令或事件語法糖 @ 為DOM元素綁定事件。Vue解析組件模板后,在綁定更新 v-on 指令時會為DOM元素綁定事件(當然如果元素為 iframe ,會等到 iframe 加載完成后再為其綁定事件)。
Vue中為DOM元素綁定事件是采用DOM2級事件的處理方式,因為Vue服務的是IE9以上的現代瀏覽器,他們也都是支持DOM2級事件。因此下例中
實際上相當于
el.addEventListener('click', func)
所以 addEventListener 支持綁定的事件, v-on 指令也都支持。同樣的理論上也可以解綁事件,雖然也有相應的 api ,但是Vue文檔中并沒有顯示地告訴我們怎么做。
在代碼中可以看到,每個 v-on 指令都有一個reset 方法, reset 方法是當指令所綁定方法發生改變時,重新綁定事件之前的解綁操作,我們可以利用這個 api 來解綁事件。因此如果需要解綁事件,我們可以遍歷 vm._directives 找到相應該指令,進行解綁。
當然既然是采用DOM2級事件處理,也可以使用 removeEventListener 直接進行解綁,看這個 demo 。 執行解綁操作后 btn1 的確解綁成功了,但 btn2 沒有解綁成功,這要說到 v-on 指令的 修飾符 ,見源碼中對帶有修飾符的 handler 的處理。顧名思義,修飾符修飾過的 handler 做了更多的事情,Vue的處理是包裝原 handler 新的 handler 用于向DOM元素綁定,而解綁時仍然解綁原方法當然會失敗。
當然這只是分析Vue的事件綁定原理,大多數情況下我們并不需要去解綁事件。合理的利用事件委托可以解決大部分由事件綁定引起的性能問題。
自定義事件
Vue自定義事件是為組件間通信設計,自定義事件提供了 $on、$off、$once、$emit、$broadcast、$dispatch 幾個 api,非常簡潔。
首先提兩個vm的私有變量,vm._events 和 vm._eventCount。每個vm實例所有的自定義事件都將存儲在 vm._events,而 vm._eventsCount 存儲的是執行事件廣播后子組件觸發自定義事件處理程序的數量,這是為了事件廣播優化而來的,如果 vm._eventsCount[event] 數量為零,當事件廣播時則可斷定子組件沒有該事件的監聽器,就沒必要向子組件層層捕獲該事件監聽器了。
$on
注冊一個自定義事件,注冊事件很簡單,首先將其掛載到該實例下
vm._events[event] = fn
然后是向上傳播,更新各個組件的 _eventsCount。這里需要注意,我們可以通過 $on 為生命周期注冊鉤子, 點擊 查看demo,但是生命周期不可冒泡和廣播,所以需要更新 eventsCount 前需要過濾。 查看modifyListenerCount
$once
因為 $once 注冊的事件是一次性的,執行完后卸載,所以其實 $once 調用 $on 來注冊事件的函數是包裝過的。
$off
理解了注冊事件的流程(其實就是更改 _events 和 _eventsCount)那么卸載事件也就很清晰了。
但是$off支持三種卸載方式
1、 如果沒有參數,則刪除所有的事件監聽器
遍歷 _events,冒泡更新每個事件的 _eventsCount,清空 vm._events
2、 如果只提供了事件,則刪除這個事件所有的監聽器
冒泡更新每個事件的 _eventsCount,vm._events 中剔除該事件
3、 如果同時提供了事件與回調,則只刪除這個回調
遍歷 vm._events[event] 的事件處理方法,如果該事件處理方法和回調相同,則從 vm._events[event] 剔除該事件處理方法,并冒泡更新該事件的 _eventsCount
$emit
觸發事件,直接遍歷 vm._events[event] 的每個事件處理程序并執行。
$emit 返回 shouldPropagate,shouldPropagate 是一個布爾值,取決于父鏈上的是否存在該事件的監聽器以及,事件處理程序返回的值。他決定 $dispatch 是否停止冒泡。
dispatch
派發事件。首先在實例上觸發該事件,默認情況下將會停止冒泡傳播,但如果 $emit 返回的 shouldPropagate 為 true,則該事件會繼續沿父鏈向上傳播,即在父組件繼續向上派發事件。
broadcast
事件廣播。深度優先遍歷子組件,并執行各個子組件的監聽器事件處理程序,在綁定和卸載自定義事件時會會每個組件維護一個 vm._eventsCount,而它的作用正是在深度遍歷的時候給予提示,避免不必要的深度遍歷。
通過自定義事件在組件之間的傳播,我們可以利用它進行組件通信。組件通信在應用開發過程中是一個棘手的問題,因為它直接關系到整個應用的健壯和可維護程度,在開發大型項目中建議引入vuex,從應用架構的角度來考慮組件通信相比這種事件形式更容易維護,比如多個子組件都有派發事件與父組件進行通信,如果子組件派發事件不注意命名規范,出現命名重復情況,那么父組件監聽器根本不知道這個事件是從哪里派發過來的以技如何處理,這是隱患之一。如果采用這種方式進行組件通信,那么必將導致子組件大量派發事件,那么父組件將要維護大量的事件監聽器,如果時間久了,很容易忘記監聽器和派發事件子組件的對應關系,這又增加了開發與維護成本。充斥著事件派發的組件維護成本也是一個容易留坑的地方。此外通過事件可以進行父子組件的通信,但兄弟組件的通信有需要增加不少開發成本。
組件的自定義事件
在上文分析DOM元素綁定事件中,我們用到這個例子
但是有時候會出現 v-on 為組件綁定事件的情況,如
上文中沒有分析到,留在這里說,這里有兩個明顯區別
是組件而不是DOM元素
自定義事件而不是DOM事件
因此顯然 addEventLisntener 不適用,而且Vue執行的也是和第一個例子完全不同的處理方式, 對其的處理在 registerComponentEvents 。它其實是為組件注冊自定義事件。這里 v-on 指令綁定的結果是 demoVm._a href="" title="成都app制作開發公司events[myfunc] = [func] 以及更新 _eventsCount。
查看這個 demo 。
可見 v-on 指令既可為DOM元素綁定事件也可為組件綁定自定義事件。明白了這個,這個issuse 的原因也就很明了了。
- 1 回答
- 0 關注
- 2428 瀏覽
添加回答
舉報