摘要:給注冊原生事件回調(diào)為統(tǒng)一的事件分發(fā)機制。根據(jù)元素唯一標(biāo)識和事件類型從中取出回調(diào)函數(shù)返回帶有合成事件參數(shù)的回調(diào)函數(shù)總流程將上面的四個流程串聯(lián)起來。可見,回調(diào)函數(shù)是直接調(diào)用調(diào)用的,并沒有指定調(diào)用的組件,所以不進行手動綁定的情況下直接獲取到的是。
關(guān)于React事件的疑問
1.為什么要手動綁定this
2.React事件和原生事件有什么區(qū)別
3.React事件和原生事件的執(zhí)行順序,可以混用嗎
4.React事件如何解決跨瀏覽器兼容
5.什么是合成事件
下面是我閱讀過源碼后,將所有的執(zhí)行流程總結(jié)出來的流程圖,不會貼代碼,如果你想閱讀代碼看看具體是如何實現(xiàn)的,可以根據(jù)流程圖去源碼里尋找。
事件注冊組件裝載 / 更新。
通過lastProps、nextProps判斷是否新增、刪除事件分別調(diào)用事件注冊、卸載方法。
調(diào)用EventPluginHub的enqueuePutListener進行事件存儲
獲取document對象。
根據(jù)事件名稱(如onClick、onCaptureClick)判斷是進行冒泡還是捕獲。
判斷是否存在addEventListener方法,否則使用attachEvent(兼容IE)。
給document注冊原生事件回調(diào)為dispatchEvent(統(tǒng)一的事件分發(fā)機制)。
事件存儲EventPluginHub負責(zé)管理React合成事件的callback,它將callback存儲在listenerBank中,另外還存儲了負責(zé)合成事件的Plugin。
EventPluginHub的putListener方法是向存儲容器中增加一個listener。
獲取綁定事件的元素的唯一標(biāo)識key。
將callback根據(jù)事件類型,元素的唯一標(biāo)識key存儲在listenerBank中。
listenerBank的結(jié)構(gòu)是:listenerBank[registrationName][key]。
例如:
{ onClick:{ nodeid1:()=>{...} nodeid2:()=>{...} }, onChange:{ nodeid3:()=>{...} nodeid4:()=>{...} } }事件觸發(fā) / 執(zhí)行
這里的事件執(zhí)行利用了React的批處理機制,在前一篇的【React深入】setState執(zhí)行機制中已經(jīng)分析過,這里不再多加分析。
觸發(fā)document注冊原生事件的回調(diào)dispatchEvent
獲取到觸發(fā)這個事件最深一級的元素
例如下面的代碼:首先會獲取到this.child
this.parent = ref}>this.child = ref}> test
遍歷這個元素的所有父元素,依次對每一級元素進行處理。
構(gòu)造合成事件。
將每一級的合成事件存儲在eventQueue事件隊列中。
遍歷eventQueue。
通過isPropagationStopped判斷當(dāng)前事件是否執(zhí)行了阻止冒泡方法。
如果阻止了冒泡,停止遍歷,否則通過executeDispatch執(zhí)行合成事件。
釋放處理完成的事件。
react在自己的合成事件中重寫了stopPropagation方法,將isPropagationStopped設(shè)置為true,然后在遍歷每一級事件的過程中根據(jù)此遍歷判斷是否繼續(xù)執(zhí)行。這就是react自己實現(xiàn)的冒泡機制。
合成事件調(diào)用EventPluginHub的extractEvents方法。
循環(huán)所有類型的EventPlugin(用來處理不同事件的工具方法)。
在每個EventPlugin中根據(jù)不同的事件類型,返回不同的事件池。
在事件池中取出合成事件,如果事件池是空的,那么創(chuàng)建一個新的。
根據(jù)元素nodeid(唯一標(biāo)識key)和事件類型從listenerBink中取出回調(diào)函數(shù)
返回帶有合成事件參數(shù)的回調(diào)函數(shù)
總流程將上面的四個流程串聯(lián)起來。
為什么要手動綁定this通過事件觸發(fā)過程的分析,dispatchEvent調(diào)用了invokeGuardedCallback方法。
function invokeGuardedCallback(name, func, a) { try { func(a); } catch (x) { if (caughtError === null) { caughtError = x; } } }
可見,回調(diào)函數(shù)是直接調(diào)用調(diào)用的,并沒有指定調(diào)用的組件,所以不進行手動綁定的情況下直接獲取到的this是undefined。
這里可以使用實驗性的屬性初始化語法 ,也就是直接在組件聲明箭頭函數(shù)。箭頭函數(shù)不會創(chuàng)建自己的this,它只會從自己的作用域鏈的上一層繼承this。因此這樣我們在React事件中獲取到的就是組件本身了。
和原生事件有什么區(qū)別React 事件使用駝峰命名,而不是全部小寫。
通過 JSX , 你傳遞一個函數(shù)作為事件處理程序,而不是一個字符串。
例如,HTML:
在 React 中略有不同:
另一個區(qū)別是,在 React 中你不能通過返回 false 來阻止默認行為。必須明確調(diào)用 preventDefault 。
由上面執(zhí)行機制我們可以得出:React自己實現(xiàn)了一套事件機制,自己模擬了事件冒泡和捕獲的過程,采用了事件代理,批量更新等方法,并且抹平了各個瀏覽器的兼容性問題。
React事件和原生事件的執(zhí)行順序componentDidMount() { this.parent.addEventListener("click", (e) => { console.log("dom parent"); }) this.child.addEventListener("click", (e) => { console.log("dom child"); }) document.addEventListener("click", (e) => { console.log("document"); }) } childClick = (e) => { console.log("react child"); } parentClick = (e) => { console.log("react parent"); } render() { return (this.parent = ref}>) }this.child = ref}> test
執(zhí)行結(jié)果:
由上面的流程我們可以理解:
react的所有事件都掛載在document中
當(dāng)真實dom觸發(fā)后冒泡到document后才會對react事件進行處理
所以原生的事件會先執(zhí)行
然后執(zhí)行react合成事件
最后執(zhí)行真正在document上掛載的事件
react事件和原生事件可以混用嗎?react事件和原生事件最好不要混用。
原生事件中如果執(zhí)行了stopPropagation方法,則會導(dǎo)致其他react事件失效。因為所有元素的事件將無法冒泡到document上。
由上面的執(zhí)行機制不難得出,所有的react事件都將無法被注冊。
合成事件、瀏覽器兼容function handleClick(e) { e.preventDefault(); console.log("The link was clicked."); }
這里, e 是一個合成的事件。 React 根據(jù) W3C 規(guī)范 定義了這個合成事件,所以你不需要擔(dān)心跨瀏覽器的兼容性問題。
事件處理程序?qū)鬟f SyntheticEvent 的實例,這是一個跨瀏覽器原生事件包裝器。 它具有與瀏覽器原生事件相同的接口,包括 stopPropagation() 和 preventDefault() ,在所有瀏覽器中他們工作方式都相同。
每個 SyntheticEvent 對象都具有以下屬性:
boolean bubbles boolean cancelable DOMEventTarget currentTarget boolean defaultPrevented number eventPhase boolean isTrusted DOMEvent nativeEvent void preventDefault() boolean isDefaultPrevented() void stopPropagation() boolean isPropagationStopped() DOMEventTarget target number timeStamp string type
React合成的SyntheticEvent采用了事件池,這樣做可以大大節(jié)省內(nèi)存,而不會頻繁的創(chuàng)建和銷毀事件對象。
另外,不管在什么瀏覽器環(huán)境下,瀏覽器會將該事件類型統(tǒng)一創(chuàng)建為合成事件,從而達到了瀏覽器兼容的目的。
推薦閱讀【React深入】setState的執(zhí)行機制
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/102379.html
摘要:以我自己的理解,函數(shù)式編程就是以函數(shù)為中心,將大段過程拆成一個個函數(shù),組合嵌套使用。越來越多的跡象表明,函數(shù)式編程已經(jīng)不再是學(xué)術(shù)界的最愛,開始大踏步地在業(yè)界投入實用。也許繼面向?qū)ο缶幊讨螅瘮?shù)式編程會成為下一個編程的主流范式。 使用React也滿一年了,從剛剛會使用到逐漸探究其底層實現(xiàn),以便學(xué)習(xí)幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。下面整理一些知識點,算是React看書...
摘要:另外第三方也可以通過的事件插件機制來合成自定義事件,盡管很少人這么做。抽象跨平臺事件機制。打算干預(yù)事件的分發(fā)。事件是的一個自定義事件,旨在規(guī)范化表單元素的變動事件。 showImg(https://segmentfault.com/img/remote/1460000019961124?w=713&h=307); 當(dāng)我們在組件上設(shè)置事件處理器時,React并不會在該DOM元素上直接綁定...
摘要:注冊事件的回調(diào)函數(shù)由來統(tǒng)一管理,根據(jù)事件的類型和組件標(biāo)識為唯一標(biāo)識事件并進行存儲。利用中注入的例如會將原生的事件轉(zhuǎn)化成合成的事件,然后批量執(zhí)行存儲的回調(diào)函,回調(diào)函數(shù)的執(zhí)行分為兩步,第一步是將所有的合成事件放到事件隊列里面,第二步是逐個執(zhí)行。 最近在閱讀《深入React技術(shù)棧》一書中,發(fā)現(xiàn)了之前使用React中并沒有注意到的React事件與瀏覽器原生事件之間的區(qū)別,鑒于好久已經(jīng)沒有寫...
摘要:調(diào)用事務(wù)的方法,遍歷待更新組件隊列依次執(zhí)行更新。執(zhí)行生命周期,根據(jù)返回值判斷是否要繼續(xù)更新。三總結(jié)鉤子函數(shù)和合成事件中在的生命周期和合成事件中,仍然處于他的更新機制中,這時為。這時將執(zhí)行之前累積的。 一.幾個開發(fā)中經(jīng)常會遇到的問題 以下幾個問題是我們在實際開發(fā)中經(jīng)常會遇到的場景,下面用幾個簡單的示例代碼來還原一下。 1.setState是同步還是異步的,為什么有的時候不能立即拿到更新結(jié)...
摘要:前言接下來讓我們進入新的章節(jié)漫談。正文一事件系統(tǒng)的事件系統(tǒng)事件系統(tǒng)符合標(biāo)準(zhǔn),不存在任何兼容性問題,并且與原生的瀏覽器事件一樣有同樣的接口。所有的事件都自動綁定到最外層。組織事件冒泡的行為只適用于合成系統(tǒng)中,且沒辦法阻止原生事件冒泡。 前言 接下來讓我們進入新的章節(jié):漫談React。本篇文章主要講React事件系統(tǒng)和表單操作。 正文 一:事件系統(tǒng) 1.react的事件系統(tǒng)react事件系...
閱讀 3104·2021-10-13 09:40
閱讀 3958·2021-09-22 15:51
閱讀 1503·2021-09-22 15:48
閱讀 1072·2021-09-06 15:00
閱讀 1796·2019-08-30 15:43
閱讀 2367·2019-08-29 18:35
閱讀 1677·2019-08-29 16:18
閱讀 3622·2019-08-29 12:49