成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

React16事件機(jī)制徹底解讀

sugarmo / 3366人閱讀

摘要:二阻止事件冒泡,沒辦法阻止原生事件冒泡。從事件的三個(gè)階段講起,會(huì)略微提及下的事件機(jī)制。就是說注冊(cè)某個(gè)事件,會(huì)強(qiáng)制依賴其他事件。因?yàn)樗惺录际墙壎ㄔ谏系摹澲攸c(diǎn)事件合成的過程。派發(fā)的過程實(shí)際上就是遍歷事件隊(duì)列的過程。

react.js事件機(jī)制

寫這篇文章的緣由:
一:在給input綁定事件的時(shí)候,很好奇為何onChange的交互形式竟然和onInput一模一樣。 因?yàn)樵腸hange事件是在input失去焦點(diǎn)的時(shí)候觸發(fā),但react的onChange則完全不同。
二:阻止事件冒泡,event.preventDeault()沒辦法阻止原生事件冒泡。 

從react事件的三個(gè)階段講起,會(huì)略微提及下react15的事件機(jī)制。
一 事件注冊(cè)
二 事件合成
三 事件派發(fā)

react事件注冊(cè)

1. 一切從createInstance,創(chuàng)建dom實(shí)例開始講起。

react16引入了fiber的概念,講fiber的文章很多,這里就不多闡述??梢院唵蔚南壤斫鉃閒iber Tree 略微等于 vDOM Tree(當(dāng)然實(shí)際肯定有差別)。

當(dāng)react遍歷tree創(chuàng)建真實(shí)dom實(shí)例的時(shí)候做了什么???

重點(diǎn)就在這幾行代碼。domElement等于真實(shí)創(chuàng)建的dom,里面調(diào)用的就是我們熟悉的createElement。而precacheFiberNode和updateFiberProps兩個(gè)方法分別給domElement(真實(shí)dom)添加了兩個(gè)屬性。所有react16項(xiàng)目中的dom都會(huì)擁有這兩個(gè)屬性,并且這個(gè)兩個(gè)屬性的屬性名在同一個(gè)項(xiàng)目中是一致的。

圖片描述

precacheFiberNode方法中 設(shè)置 node[internalInstanceKey] = 一個(gè)new FiberNode()的實(shí)例

updateFiberProps方法中 設(shè)置node[internalEventHandlersKey] = props 。這里的props就是 這里的屬性。

劃重點(diǎn)了! 這兩個(gè)方法等于將真實(shí)dom和fiber,props直接關(guān)聯(lián)到了一起,相互引用,這點(diǎn)很重要,后續(xù)會(huì)用到,相當(dāng)于前期準(zhǔn)備。

2. listenTo
createInstance后(仍然在fiber tree遍歷中),程序兜兜轉(zhuǎn)轉(zhuǎn),層層調(diào)用,最后終于走到了關(guān)鍵方法listenTo這里來,所有的事件注冊(cè)邏輯都在這里實(shí)現(xiàn)。react做了一系列的兼容處理,盡可能的保證各個(gè)瀏覽器端交互一致。

ensureListeningTo里面調(diào)用的就listenTo。首先去遍歷props中的屬性。而registrationNameModules.hasOwnProperty(propKey),registrationNameModules是一個(gè)事件名的集合,幾乎包含了所有的常見事件,這也就是如果你寫一些稀奇古怪的事件,react是不識(shí)別的。如果判定props中的屬性 如onClick在registrationNameModules中,并且值typeof === function,則會(huì)進(jìn)入到listenTo中。

回到listenTo這個(gè)方法,他接收兩個(gè)參數(shù),一個(gè)是事件名如onClick,一個(gè)是contentDocumentHandle,通常就是document。
重點(diǎn)講下這個(gè) var dependencies = registrationNameDependencies[registrationName]; registrationNameDependencies這個(gè)東西理解為事件依賴。 就是說注冊(cè)某個(gè)事件,react會(huì)強(qiáng)制依賴其他事件。而具體是哪些依賴,react的event模塊已經(jīng)幫我們處理了,就不深層次探討。
舉例onChange事件就依賴了下面的一些事件
registrationNameDependencies = {

onChange = ["topBlur", "topChange", "topClick", "topInput", "topKey" ....還有] 

}
這里react的事件都加上了top前綴,沒什么太大的深層次含義,可能就是為了區(qū)分下吧,畢竟后續(xù)用到的時(shí)候,react會(huì)再次把它轉(zhuǎn)回來的。如topInput轉(zhuǎn)成input之類的。
接著往下走dependencies得到的是一個(gè)依賴事件數(shù)組,隨即遍歷這個(gè)數(shù)組,做一些hack處理,然后會(huì)調(diào)用這個(gè)方法trapBubbledEvent。
trapBubbledEvent(dependency, topLevelTypes[dependency], mountAt) ,三個(gè)參數(shù) dependency= topClick。 上面提到過的,會(huì)把top前綴什么的再次轉(zhuǎn)回來, 所以topLevelTypes[dependency]就是click。mountAt就等于document(不考慮多個(gè)window)。那trapBubbledEvent又做了什么呢?

這里說白就是調(diào)用下面這個(gè)方法

熟悉了吧。這就是我們常見的事件綁定了。。
又到劃重點(diǎn)時(shí)間了
listenTo做的事情很簡單,就是遍歷props中的event,然后將事件和事件的依賴事件統(tǒng)統(tǒng)掛載到document上,并且所有的事件的回調(diào)函數(shù)走的都是dispatchEvent。
打個(gè)比方,如果我綁定一個(gè)onChange事件,那么react不僅僅只綁定一個(gè)onChange事件到document上,還會(huì)綁定許多依賴事件上去,如focus,blur,input等等。是不是看出點(diǎn)什么苗頭。onChange事件依賴了onInput事件,但這還并不是 為什么onChange的表現(xiàn)形式和onInput一樣的 全部原因。 這只是為后續(xù)的合成提供了依賴。
整個(gè)事件注冊(cè)差不多就到這里為止了。react會(huì)把所有的事件都掛載到document上。這也是為什么我們event.stoppropagation()為什么不能阻止原生冒泡。因?yàn)樗惺录际墙壎ㄔ赿ocument上的。意味著你的原生事件都執(zhí)行完了之后,才能執(zhí)行document的事件。dispatchEvent會(huì)做統(tǒng)一的派發(fā)??梢哉f原生事件的執(zhí)行順序是早于react事件的。

稍微提及下react15的事件注冊(cè)和16完全不同,16有一個(gè)listenBink的感念,所以的事件都注冊(cè)到這個(gè)對(duì)象里面,并且由react_id關(guān)聯(lián)起來。但16已經(jīng)舍棄了react_id的感念。

react事件合成

以一個(gè)input輸入框?yàn)槔?,?dāng)用戶輸入了數(shù)字1之后,react做了什么。
首先回到上面說的disPatchEvent中。所有的事件回調(diào)統(tǒng)統(tǒng)都是這個(gè)函數(shù),現(xiàn)在我們看看這個(gè)函數(shù)做了什么

dispatchEvent接收兩個(gè)參數(shù) topLevelType(事件topFocus之類的) nativeEvent就是原生的event對(duì)象。
dispatchEvent里面繞得有點(diǎn)深,跳來跳去。最后會(huì)走到下面這個(gè)方法里面來

handleTopLevel接收4個(gè)參數(shù),targetInst就是fiber實(shí)例,上面提到過每個(gè)dom中都會(huì)掛載兩個(gè)屬性,其中一個(gè)保存了fiber的引用。過程就是根據(jù)dispatchEvent得到的一個(gè)nativeEvent,可以得到
一個(gè)真正觸發(fā)事件的nativeEventTarget元素(event.target),然后取得fiber引用即可。 這樣handleTopLevel四個(gè)關(guān)鍵參數(shù)都齊全了。
handleTopLevel又做了什么呢。。根據(jù)里面方法的字面意思 無非是提取event對(duì)象(react的合成對(duì)象,并非原生的event),然后放到事件隊(duì)里去。
重點(diǎn)來說說extractEvents方法,所有的奧秘都在這里了。。。它接收了handleTopLevel的四個(gè)參數(shù)。

這里可能有點(diǎn)繞,需要理解下plugins。簡單的解釋就是react的event模塊所包含的eventPliguns。好像有6個(gè)左右的plugin吧,或許是不同的組件處理不同的事件類型吧。具體實(shí)現(xiàn)和功能就沒必要說了,太底層。
比如用戶在input輸入的過程,或許第一步是觸發(fā)了某個(gè)元素的blur,然后是input的focus,然后是keydown,input之類等等,順序就是按照瀏覽器的事件順序。
我們拿input事件舉例,撇開其他無關(guān)事件(注:這里會(huì)解釋 最開始提到的第一個(gè)問題)。

 。

劃重點(diǎn)了。。
上面的Input,雖然我們只注冊(cè)了一個(gè)onChange,但根據(jù)我們前面的了解,react會(huì)注冊(cè)依賴事件,onChange會(huì)依賴onInput,因此同樣會(huì)注冊(cè)onInput事件。
當(dāng)input事件被觸發(fā)的時(shí)候,遍歷plugin去處理事件。并返回一個(gè)由plugin合成的event
events = accumulateInto(events, extractedEvents); 看這里,events是一個(gè)數(shù)組,accumulateInto等同于events.push(extractedEvents);
重點(diǎn)來 plugin處理onInput事件的時(shí)候,會(huì)生成兩個(gè)event,一個(gè)是input,一個(gè)是change, 會(huì)生成兩個(gè)event,一個(gè)是input,一個(gè)是change, 會(huì)生成兩個(gè)event,一個(gè)是input,一個(gè)是change,
重要的事情說三遍,記住這個(gè)地方。后面是關(guān)鍵。
回到plugin這里來,進(jìn)入到possiblePlugin.extractEvents里面去,這個(gè)函數(shù)返回一個(gè)event,并且在層層調(diào)用后執(zhí)行了一個(gè)至關(guān)重要的函數(shù)traverseTwoPhase ,讓我們走進(jìn)去看看這個(gè)函數(shù)
究竟做了什么?

traverseTwoPhase接收三個(gè)參數(shù),inst = fiber實(shí)例 fn = accumulateDirectionalDispatches函數(shù),等會(huì)會(huì)著重講解這個(gè)方法。arg = event。這個(gè)event是possiblePlugin.extractEvents中生成的event對(duì)象,它把這個(gè)event當(dāng)參數(shù)傳遞到更深層的方法里面,是為了在event上掛載兩個(gè)極為重要的屬性,等下細(xì)說。
traverseTwoPhase 具體做了什么呢?
while循環(huán),取到fiber(觸發(fā)事件的真正target所對(duì)應(yīng)的虛擬dom)的所有父節(jié)點(diǎn),等同于得到了一棵fiberTree。
如下

 {console.log(1111)}}>
     {console.log(2222)}}>
         {console.log(3333)}} />
    

假設(shè)有上面三個(gè)組件ComponentA, ComponentB, ComponentC。層層嵌套。那么path就等于[ComponentC, ComponentB, ComponentA];
traverseTwoPhase 中執(zhí)行了兩次循環(huán),一次為captured捕獲,即執(zhí)行順序從A-C。一次為bubbled冒泡,執(zhí)行順序?yàn)閺腃-A。這里我們不考慮captured。詳細(xì)講解下冒泡過程。上面
說過了fn = accumulateDirectionalDispatches,我們看看這里到底做了什么

重點(diǎn)看這里 var listener = listenerAtPhase(inst, event, phase); 這里就是取當(dāng)前的dom有沒有注冊(cè)對(duì)應(yīng)事件的listener。粗略解釋下取得的過程就是利用了最上面說過的綁定在dom
上的props,如果listener存在,則將當(dāng)前的listener和inst(理解為虛擬dom或者fiber實(shí)例)分別掛載到event下的兩個(gè)數(shù)組里。 這里極其重要
注意啦。 accumulateDirectionalDispatches 這個(gè)函數(shù)是在 path的循環(huán)里執(zhí)行的?;氐缴厦娴膒ath等于[ComponentC, ComponentB, ComponentA]的例子。因?yàn)槲覀兊腁BC三個(gè)組件都注冊(cè)了對(duì)應(yīng)的listener,故而event下的_dispatchListeners和_dispatchInstances都存儲(chǔ)有其inst和listner。至此event算是合成完畢了,控制權(quán)回到possiblePlugin.extractEvents這里。

劃重點(diǎn) : 事件合成的過程。首先根據(jù)觸發(fā)事件的target得到inst,然后遍歷他的所有父節(jié)點(diǎn)(fiber.return屬性),存儲(chǔ)在局部遍歷path中,記住這個(gè)path是有順序關(guān)系的(后面可以解釋react事件是如何阻止冒泡的)。得到path后進(jìn)行遍歷,假設(shè)遍歷的組件同樣注冊(cè)了對(duì)應(yīng)事件的listener,那么就掛載到event的_dispatchListeners和_dispatchInstances中去,這兩個(gè)屬性至關(guān)重要,后續(xù)的事件派發(fā)就是根據(jù)這兩個(gè)屬性進(jìn)行的。 注意只有注冊(cè)了對(duì)應(yīng)事件的listener,才會(huì)掛載到event里面去。比如剛剛我們的ABC都綁定了Click,自然都會(huì)push到_dispatchListeners中去。

回憶下上面剛剛說到的一個(gè)很繞的地方。 假設(shè)我給一個(gè)input綁定了onChange事件,那么react會(huì)綁定很多依賴事件到document上。其中就有input事件。 但當(dāng)我們觸發(fā)input的時(shí)候,react是怎么觸發(fā)到onChange的listener呢? 。重點(diǎn)就是剛剛說了三遍的地方,在合成input事件的時(shí)候,react會(huì)生成兩個(gè)event,一個(gè)是input,一個(gè)是change,也就是說change的那個(gè)event掛載的_dispatchListeners里面存儲(chǔ)了我們的listener。后續(xù)派發(fā)的時(shí)候,會(huì)執(zhí)行這個(gè)事件隊(duì)列,對(duì) 隊(duì)列里的event進(jìn)行派發(fā)。 這也就很好的解釋了為什么我們的change交互和input一模一樣。

事件派發(fā)

事件合成之后就是事件派發(fā)了,雖然我分開來講,當(dāng)兩個(gè)過程是緊跟著的,合成事件后,所有的事件都會(huì)push到eventQueue里面去。派發(fā)的過程實(shí)際上就是遍歷事件隊(duì)列的過程。
遍歷的過程同樣有點(diǎn)繞,我們只看關(guān)鍵的地方

有么有看到我們熟悉的地方。_dispatchListeners和_dispatchInstances這兩個(gè)里面保存了我們的inst和listener。**方法對(duì)dispatchListeners進(jìn)行了遍歷,event.isPropagationStopped()
這個(gè)地方也就是我們可以阻止合成事件冒泡的原因呢。。** 因?yàn)閐ispatchListeners同樣是按照冒泡的順序插入的,就拿剛剛的ABC三個(gè)組件來說。假設(shè)對(duì)B進(jìn)行了阻止冒泡。那么A的onClick就沒辦法
執(zhí)行了。
遍歷過程中拿到inst和對(duì)應(yīng)的listener后,執(zhí)行executeDispatch,后續(xù)的代碼就簡單直接了。

創(chuàng)建了個(gè)虛假的dom,綁定個(gè)自定義事件,然后再自己監(jiān)聽,再自己createEvent,再dispatch,觸發(fā)callback,然后在callback里面觸發(fā)真正的listener,同時(shí)會(huì)把合成的event傳遞進(jìn)去。
最后在清空重置各種數(shù)據(jù)。。大結(jié)局了。

事件注冊(cè),事件合成,事件派發(fā),三個(gè)階段,這里就不再總結(jié)了,每一階段后都有個(gè)總結(jié)的。
講真react代碼有點(diǎn)復(fù)雜,看不到的多看幾遍吧。
寫的不對(duì)的地方指正下。有不懂的提問。。碼這么多不容易了,可能有些單詞拼錯(cuò)了,包容下。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107906.html

相關(guān)文章

  • 結(jié)合源碼徹底理解 react事件機(jī)制原理 01 - 對(duì)事件機(jī)制的初步理解和驗(yàn)證

    摘要:前言這是事件機(jī)制的第一篇,主要內(nèi)容有表象理解,驗(yàn)證,意義和思考。因?yàn)楹铣墒录挠|發(fā)是基于瀏覽器的事件機(jī)制來實(shí)現(xiàn)的,通過冒泡機(jī)制冒泡到最頂層元素,然后再由統(tǒng)一去處理。合成事件的阻止冒泡不會(huì)影響原生事件。 showImg(https://segmentfault.com/img/bVbtvP2?w=800&h=420); 前言 這是 react 事件機(jī)制的第一篇,主要內(nèi)容有:表象理解,驗(yàn)證...

    muddyway 評(píng)論0 收藏0
  • 結(jié)合源碼徹底理解 react事件機(jī)制原理 02 - 對(duì)于合成的理解

    摘要:前言這是事件機(jī)制系列文章的第二篇對(duì)于合成的理解,咱們就來說說合成這個(gè)名詞。在給注冊(cè)事件的時(shí)候也是對(duì)兼容性做了處理??偨Y(jié)以上就是我對(duì)于合成這個(gè)名詞的理解,其實(shí)內(nèi)部還處理了很多,我只是略微簡單的舉了幾個(gè)栗子。 showImg(https://segmentfault.com/img/bVbtvI3?w=1048&h=550); 前言 這是react事件機(jī)制系列文章的第二篇-對(duì)于合成的理解,...

    nihao 評(píng)論0 收藏0
  • 前端小冊(cè) - 結(jié)合源碼徹底理解 react 事件機(jī)制

    摘要:對(duì)事件機(jī)制的初步理解和驗(yàn)證對(duì)于合成的理解事件注冊(cè)機(jī)制事件執(zhí)行本文基于進(jìn)行分析,雖然不是最新版本但是也不會(huì)影響我們對(duì)事件機(jī)制的整體把握和理解。最后希望通過本文可以讓你對(duì)事件機(jī)制有更清晰的認(rèn)識(shí)和理解。 showImg(https://segmentfault.com/img/bVbtvI3?w=1048&h=550); 前言 寫這個(gè)文章也算是實(shí)現(xiàn)19年的一個(gè) flag,研究一個(gè)知識(shí)點(diǎn)并且把...

    YJNldm 評(píng)論0 收藏0
  • 結(jié)合源碼徹底理解 react事件機(jī)制原理 03 - 事件注冊(cè)

    摘要:文章涉及到的源碼是基于版本,雖然不是最新版本但是也不會(huì)影響我們對(duì)事件機(jī)制的整體把握和理解。到這里事件注冊(cè)就完事兒了。 showImg(https://segmentfault.com/img/bVbtvI3?w=1048&h=550); 前言 這是 react 事件機(jī)制的第三節(jié) - 事件注冊(cè),通過本文你將了解react 事件的注冊(cè)過程,以及在這個(gè)過程中主要經(jīng)過了哪些關(guān)鍵步驟,同時(shí)結(jié)合源...

    chaosx110 評(píng)論0 收藏0
  • 結(jié)合源碼徹底理解 react事件機(jī)制原理 04 - 事件執(zhí)行

    摘要:文章涉及到的源碼是基于版本,雖然不是最新版本但是也不會(huì)影響我們對(duì)事件機(jī)制的整體把握和理解??偨Y(jié)本文主要是從整體流程上介紹了下事件觸發(fā)的過程。 showImg(https://segmentfault.com/img/bVbtvI3?w=1048&h=550); 前言 這是 react 事件機(jī)制的第四節(jié)-事件執(zhí)行,一起研究下在這個(gè)過程中主要經(jīng)過了哪些關(guān)鍵步驟,本文也是react 事件機(jī)制...

    marser 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<