本文源於本人在學習react
過程當中遇到的一個問題;本文內容爲本人的一些的理解,若有不對的地方,還請你們指出來。本文是講react的事件,不是介紹其api,而是猜測一下react
合成事件的實現方式react
class EventTest extends Component { handleParentClick(e) { console.log('click parent div'); } handleChildClick(e) { e.stopPropagation(); console.log('click child div'); } componentDidMount() { document.querySelector('.parent').addEventListener('click', this.handleParentClick); } render() { return ( <div className="parent"> <div className="child" onClick={this.handleChildClick}></div> </div> ); } }
上述代碼render
出來後,嘗試點擊一下div.child
,詭異的現象產生了:web
控制檯中輸出如上圖所示,這徹底不符合瀏覽器的事件執行啊,我所指望的是指輸出click child div
,由於已經利用了e.stopPropagation()
來阻止冒泡,說明阻止冒泡失效了,可是僅僅如此嗎,能夠發現的是首先輸出的是click parent div
(wtf)。chrome
爲了解決上述問題,先來了解下react
的事件,react
事件是合成事件,爲原生事件的一個子集,僅僅是進行了一個跨瀏覽器的封裝。可是真的只有這麼簡單?圖樣圖森破。
利用控制檯,看下div.child
對應的事件處理函數:api
一個空函數,事件的監聽函數不是所定義的handleChildClick
,而是emptyFunction
,也就是說react
沒有在真實的DOM
節點上綁定事件(在DOM
節點上綁定事件比較消耗內存,由於當dom
節點被remove
後,雖然不存在與dom tree
中,可是仍存在與內存中,須要手動remove
事件orchild = null
),react
的合成事件利用的是事件代理方式實現,也就是說會將事件監聽器綁定到整個文檔document
上,是否是這樣呢?來驗證一下,利用chrome:瀏覽器
能夠發現,document
上的確被綁定了click
事件,dom
節點的真實的事件處理函數所有以一個特定的結構存儲在了內存中,當點擊div.child
時,這時其事件處理函數爲emptyFunction
,執行這個函數無任何做用,按照瀏覽器標準事件模型,開始向上冒泡,這時到了div.parent
,因而輸出了click parent div
,一直向上到了document
上,這時根據e.target
進行處理,而react
並不會根據dom
層級式傳播那樣遍歷virtual dom
結構,這樣有時遍歷的層級會不少,並且會有不少的無效遍歷。dom
react
是怎麼作的呢?react
依靠每一個React component
各自獨立的id
來編碼這個層級。這樣就能經過簡單的字符串操做來獲取全部父級 component 的父級內容,再把事件監聽存儲在hashmap當中,好比有以下結構而且爲沒一層div
添加onClick
函數
div.a div.b div.c
當點擊div.c
時,處理方式:學習
clickBubbleListeners['a.b.c'](event); clickBubbleListeners['a.b'](event); clickBubbleListeners['a'](event);
在合成事件中用e.stopPropagation
只能阻斷上述冒泡過程。this
由此能夠看出:編碼
阻止react
事件冒泡的行爲只能用於react
合成事件中,對於原生事件無效(合成事件中的e.stopPropagation
與原生事件中的e.stopPropagation
並非一回事)
阻止原生事件的冒泡行爲,能夠阻止react
合成事件的傳播(根本不會冒泡到document
上,因此不會觸發react
的合成事件)
在寫react
時,最好不要將合成事件與原生事件混用。
本文部分參考自IMWeb—React事件初探