摘要:前提最近通過閱讀官方文檔的事件模塊,有了一些思考和收獲,在這里記錄一下調(diào)用方法時需要手動綁定先從一段官方代碼看起代碼中的注釋提到了一句話的綁定是必須的,其實這一塊是比較容易理解的,因為這并不是的一個特殊點,而是這門語言的特性。
前提
最近通過閱讀React官方文檔的事件模塊,有了一些思考和收獲,在這里記錄一下~
調(diào)用方法時需要手動綁定this先從一段官方代碼看起:
代碼中的注釋提到了一句話:
This binding is necessary to make this work in the callback
this的綁定是必須的,其實這一塊是比較容易理解的, 因為這并不是React的一個特殊點, 而是Javascript這門語言的特性。
可以看到,調(diào)用的是this.handleClick函數(shù),handleClick函數(shù)里面又讀取到了this屬性,但是該函數(shù)的調(diào)用位置又是在render函數(shù)里面,render返回的是一個JSX,最后經(jīng)過babel編譯成調(diào)用React.createElement函數(shù),
在這之前,我們掌握的是this永遠(yuǎn)指向的是最后調(diào)用它的對象,經(jīng)過這樣的一個轉(zhuǎn)換, 實際上this最后指向的是undeined了, 那么調(diào)用handleClick函數(shù)自然會報錯。
當(dāng)然,如果你不在函數(shù)里面使用this的話,通常會沒事,但并不建議這么做。
關(guān)于this的指向與function的原理,推薦閱讀 how functions work in JavaScript
既然知道了是因為this的指向原因而采用綁定的做法,那當(dāng)然可以用箭頭函數(shù)來解決了,箭頭函數(shù)中的this是在定義函數(shù)的時候綁定,也就是說this是繼承自父執(zhí)行上下文,如下:
這樣this也能達(dá)到我們的預(yù)期效果
先從官方上的一段話看起,他的意思是合成事件是React根據(jù)W3C標(biāo)準(zhǔn)定義的,無需擔(dān)心瀏覽器之間的差異
Here, e is a synthetic event. React defines these synthetic events according to the W3C spec, so you don’t need to worry about cross-browser compatibility
樣看起來React的合成事件只是兼容瀏覽器? 答案當(dāng)然是遠(yuǎn)遠(yuǎn)不止啦!
在探尋其優(yōu)點之前,我們先看一下其是怎樣的一個機(jī)制。
React的事件機(jī)制其實網(wǎng)上有很多同學(xué)都分析過了, 他并沒有將事件注冊在對應(yīng)的元素或者組件上面,而是通過委托的方式,將所有的事件都注冊到了document對象上,并統(tǒng)一調(diào)用一個dispatch回調(diào)函數(shù),其流程圖如下
我們也可以從一個實際的簡單例子看看:
我們把回調(diào)函數(shù)綁定到了button上,但是在事件上卻沒有看到button元素, 但是卻有document,并且可以看到他的回調(diào)函數(shù)就是dispatchInteractiveEvent
最后觸發(fā)事件的回調(diào)函數(shù)時,在原生的DOM會傳入一個事件屬性event,但是因為React將 所有事件委托給document處理, 那么這個event就和我們想要的不一樣,如target指向的是document,于是React就有了自己的一個合成事件,通過一個叫SyntheticEvent的基類來生成所需要的事件屬性,并傳入回調(diào)函數(shù)作為方法。
說到底,React就是把所有事件委托給document處理, 那么這樣做有什么好處:
可以統(tǒng)一在組件掛載和卸載時做處理
只需要注冊一個事件即可,節(jié)省內(nèi)存開銷
可以手動控制事件流程,特別是對state的batch處理(參考React系列的setState)
可以統(tǒng)一在組件掛載和卸載時做處理
只需要注冊一個事件即可,節(jié)省內(nèi)存開銷
可以手動控制事件流程,特別是對state的batch處理(參考React系列的setState)
事件屬性會在事件調(diào)用后被回收,即不能異步訪問老規(guī)矩,先上一段代碼:
可以看到在setTimeout函數(shù)中,訪問事件屬性是null。這是為啥?
其實這也是合成事件的一個優(yōu)化手段。 React會在事件調(diào)用完成后清理掉屬性,否則每點擊一次就生成一個事件,那么內(nèi)存的開銷會越來越大,具體的代碼可以在后面的源碼分析中看到:
當(dāng)然了, React也可以手動設(shè)置不回收,如下:
If you want to access the event properties in an asynchronous way, you should call event.persist() on the event
我們可以通過調(diào)用event,persist來設(shè)置不回收。
事件機(jī)制的源碼分析 注冊階段首先在某一個任務(wù)單元fiber調(diào)用compeleteWork函數(shù)時, React會判斷其是否具有事件屬性, 如果有則調(diào)用ensureListeningTo函數(shù)
ensureListeningTo函數(shù)主要是獲取到document對象, 并調(diào)用listenTo函數(shù)
listerTo函數(shù) 主要是通過調(diào)用trapBubbledEvent或者trapCapturedEvent將事件放在document事件上監(jiān)聽
trapBubbledEvent主要是監(jiān)聽事件, 但也可以看出, 所有事件最后觸發(fā)的都是注冊在document上的dispatch函數(shù)
dispatch函數(shù), 主要是獲取實際觸發(fā)的元素以及對應(yīng)的fiber, 最后調(diào)用batchedUpdates函數(shù), batchedUpdates函數(shù)里面的邏輯主要是關(guān)于setState的,這里主要是看事件機(jī)制, 只要知道最后調(diào)用的是handleTopLevel(bookkeeping)就好
handleTopLevel函數(shù)主要是拿到需要觸發(fā)事件的相關(guān)fiber, 并調(diào)用runExtractedEventsInBatch函數(shù)
extractEvents函數(shù)是一個生成React事件的函數(shù),React事件是通過繼承一個通用類SyntheticEvent生成的,如一個鼠標(biāo)事件的生成
React事件內(nèi)部做了優(yōu)化, 只要生成過SyntheticMouseEvent類, 就會再釋放事件的時候?qū)⑦@個類存儲起來,在下一個事件觸發(fā)時可以直接使用
React生成事件后, 會調(diào)用accumulateTwoPhaseDispatches(event)函數(shù),該函數(shù)一直追溯下去, 最后會調(diào)用traverseTwoPhase函數(shù),
traverseTwoPhase函數(shù)主要是獲取祖先組件的fiber, 并進(jìn)行捕獲和冒泡的階段處理
accumulateDirectionalDispatches函數(shù)相對簡單, 就是把fiber上對應(yīng)的事件函數(shù)賦值給evnet的_dispatchListeners屬性
React事件獲取完成后, 回到runExtractedEventsInBatch函數(shù)繼續(xù)調(diào)用runEventsInBatch(events, false); 函數(shù)的中間作了一系列的處理, 但最后執(zhí)行的是executeDispatchesAndRelease函數(shù)
executeDispatchesAndRelease函數(shù)會在執(zhí)行完事件后判斷用戶是否有設(shè)置不銷毀事件, 如果沒有, 則銷毀事件并保存事件類, 一個事件類實例一次并重復(fù)使用, 這也是為什么官方提到事件屬性只能在當(dāng)前循環(huán)中讀到
繼續(xù)往下走, 最后執(zhí)行的函數(shù)是invokeGuardedCallbackDev, 該函數(shù)通過注冊一個自定義的元素
通過Fiber中的屬性, 將事件統(tǒng)一委托 注冊到document上,并為document注冊相應(yīng)的事件回調(diào)函數(shù) dispatch函數(shù)。
先獲取實際觸發(fā)元素對應(yīng)的fiber.
生成相應(yīng)的React事件屬性event,將對應(yīng)的回調(diào)函數(shù)賦值給event._dispatchListeners, 將fiber賦值給event._dispatchInstances
通過fiber向上遍歷, 找到所有的祖先fiber, 并按原生事件的機(jī)制先捕獲后冒泡的執(zhí)行事件
注冊一個react節(jié)點, 為其注冊一個監(jiān)聽事件并觸發(fā)來執(zhí)行事件回調(diào)函數(shù)
最后,根據(jù)用戶的設(shè)置, 決定是否釋放事件。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/101593.html
摘要:前言自從發(fā)布之后,更新速度日新月異,而生命周期也隨之改變,雖然原有的一些生命周期函數(shù)面臨廢棄,但理解其背后更新的機(jī)制也是一種學(xué)習(xí)在這里根據(jù)官方文檔以及社區(qū)上其他優(yōu)秀的文章進(jìn)行一個對于生命周期的總結(jié),大致上分為以下三個模塊新老生命周期的區(qū)別為 前言 自從React發(fā)布Fiber之后,更新速度日新月異,而生命周期也隨之改變,雖然原有的一些生命周期函數(shù)面臨廢棄,但理解其背后更新的機(jī)制也是一種...
摘要:簡單的舉下例子如等生命周期以及的事件即為異步更新,這里不顯示具體代碼。因為只有當(dāng)父組件后才傳給子組件,那么如果要變成同步的,就需要放棄。 前言 在看React的官方文檔的時候, 發(fā)現(xiàn)了這么一句話,State Updates May Be Asynchronous,于是查詢了一波資料, 最后歸納成以下3個問題 setState為什么要異步更新,它是怎么做的? setState什么時候會...
摘要:簡介是一種搭建客戶端的應(yīng)用架構(gòu),更像是一種模式而不是一個框架。 簡介 Flux是一種搭建WEB客戶端的應(yīng)用架構(gòu),更像是一種模式而不是一個框架。 特點 單向數(shù)據(jù)流 showImg(https://segmentfault.com/img/remote/1460000018128072?w=1300&h=708); 與MVC的比較 1.傳統(tǒng)的MVC如下所示(是一個雙向數(shù)...
摘要:系列文章系列第一篇基礎(chǔ)雜記系列第二篇插件機(jī)制雜記系列第三篇流程雜記前言本身并不難,他所完成的各種復(fù)雜炫酷的功能都依賴于他的插件機(jī)制。的插件機(jī)制依賴于一個核心的庫,。是什么是一個類似于的的庫主要是控制鉤子函數(shù)的發(fā)布與訂閱。 系列文章 Webpack系列-第一篇基礎(chǔ)雜記 Webpack系列-第二篇插件機(jī)制雜記 Webpack系列-第三篇流程雜記 前言 webpack本身并不難,他所完成...
摘要:三種的區(qū)別即對應(yīng)中的值,如,服務(wù)器對任務(wù)都返回同一個,具體的路徑由瀏覽器區(qū)分,因為瀏覽器不會發(fā)送后面的值給服務(wù)器。如果是即變成這樣,,所以要對服務(wù)器配置不同的返回不同的資源。就是沒有的情況,比如。 三種Router的區(qū)別 1. HashRouter: 即對應(yīng)url中的hash值,如xx.com/#/a、xx.com/#/a/b, 服務(wù)器對任務(wù)url都返回同一個url,具體的路徑由瀏覽器...
閱讀 2218·2021-09-02 15:11
閱讀 1546·2019-08-30 15:43
閱讀 2085·2019-08-29 13:48
閱讀 2804·2019-08-26 13:55
閱讀 2112·2019-08-23 15:09
閱讀 2908·2019-08-23 14:40
閱讀 3439·2019-08-23 14:23
閱讀 2649·2019-08-23 14:20