天天看點

React 事件初探

作者:朱靈子 React 是一個 Facebook 和 Instagram 用來建立使用者界面的 JavaScript 庫。 創造 React 是為了解決一個問題:建構随着時間資料不斷變化的大規模應用程式。

本文初探react的頂層事件代理機制~

React采用的是頂層的事件代理機制,能夠保持事件冒泡的一緻性,可以跨浏覽器執行,甚至可以在IE8中使用HTML5的事件。

React 實作了一個“合成事件”層,這個事件層消除了 IE 與 W3C 标準實作之間的相容問題。首先區分原生事件與合成事件,我們在 componentDidMount 方法裡面通過 addEventListener 綁定的事件就是浏覽器原生事件,使用原生事件的時候注意在 componentWillUnmount 解除綁定 removeEventListener,所有通過 JSX 這種方式綁定的事件都是綁定到“合成事件”。

“合成事件”會以事件委托(event delegation)的方式綁定到元件最上層,并且在元件解除安裝(unmount)的時候自動銷毀綁定的事件。

在 DOM 節點上綁定事件比較消耗記憶體, React 則實作了一遍符合 W3C 規範的事件系統。接下來介紹該事件系統的實作原理, 事件 監聽器被綁定到整個文檔的根節點上。當事件被觸發, 浏覽器會給出一個觸發目标事件的 DOM 節點。為了在 DOM 的層級傳播事件, React 不會疊代 virtual DOM 的層級,而是依靠每個 React component 各自獨立的 id 來編碼這個層級。我們能通過簡單的字元串操作來擷取所有父級 component 的父級内容,再把事件監聽存儲在hashmap當中。下面的例子展示了事件廣播到整個virtual DOM時的傳播流程。

浏覽器為每個事件和每個listener建立一個新的事件對象,我們可以從這個事件對象擷取到事件的引用,但是這些事件對象也意味着高額的記憶體配置設定。為了減輕垃圾回收的負擔,React 在啟動時就為那些對象配置設定了一個記憶體池,當我們需要用到某一個事件對象時就可以從這個記憶體池進行複用。

框圖中的ReactBrowserEventEmitter主要用于連接配接頂層事件偵聽器,例如:

接下來是對react事件系統原理框圖的了解:

Top-level delegation用于捕獲最原始的浏覽器事件,它主要由ReactEventListener負責,ReactEventListener被注入後可以支援插件化的事件源,這一過程發生在主線程。

我們對各種事件進行去重複性處理以相容不同的浏覽器,這一過程是由工作線程來完成的。

最後我們轉發所有的本地事件到EventPluginHub(這些本地事件由相關頂級類型來捕獲),EventPluginHub會注解每個事件,然後分派事件。

React中的props代表父級分發下來的屬性,state代表元件内部可以自行管理的狀态,并且整個React沒有資料向上回溯的能力,也就是說資料隻能單向向下分發,或者自行内部消化。子元件改變父元件state的辦法隻能是通過onClick等事件觸發父元件聲明好的回調,也就是父元件提前聲明好函數或方法作為契約描述自己的state将如何變化,再将它同樣作為屬性交給子元件使用。

這樣資料總是單向從頂層向下分發的,隻有子元件回調在概念上可以回到state頂層影響資料,這樣state一定程度上是響應式的。為了面臨所有可能的擴充問題,最容易想到的辦法就是把所有state集中放到所有元件頂層,然後分發給所有元件。

React基于VirtualDom建構,可以更快、更有效地完成Dom操作。React實作了一套完整的事件合成機制,能夠保持事件冒泡的一緻性,同時可以實作跨浏覽器執行,甚至可以在IE8中使用HTML5的事件。《Secrets of the JavaScript Ninja》中講解了如何模拟 submit/focus/blur 等事件的冒泡,還講述了mouseenter 與 mouseleave 等事件的模拟。除Firefox浏覽器外都可使用支援冒泡的 focusin/focusout 來代替 focus/blur 事件,Firefox會在捕獲階段監聽 focus/blur 事件。

submit/reset 事件會在滑鼠點選或者按Enter鍵時觸發,是以可以監聽冒泡的 click 和 keypress 事件,并判斷觸發事件的元素是否為一個 form 元素的後代節點,然後手動觸發 submit/reset 事件。在Firefox v8.0浏覽器下,如果作為top-level listener之一的onmousemove事件不是挂載在document元素上,那麼當滑鼠在不是該節點或者該節點所對應的子節點元素上移動時,onmousemove事件就不會被觸發。根據不同的浏覽器對onmouseover事件、onscroll事件以及focusin、focusout事件的支援情況的不同,react進行了有針對性的處理,以下為react事件系統跨浏覽器執行的部分代碼實作:

原文連結:http://ivweb.io/topic/58227d0a0fea59e31b98bb5f