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