結合源碼完全理解 react事件機制原理 03 - 事件註冊

clipboard.png

前言

這是 react 事件機制的第三節 - 事件註冊,經過本文你將瞭解react 事件的註冊過程,以及在這個過程當中主要通過了哪些關鍵步驟,同時結合源碼進行驗證和加強理解。前端

文章涉及到的源碼是基於 react15.6.1版本,雖然不是最新版本可是也不會影響咱們對 react 事件機制的總體把握和理解。node

文中不會說很是細節的內容,而是會把大概的流程和原理性的內容進行介紹,作到對總體流程有個認知和理解。react

內容大綱數組

  1. 主要作兩件事 (事件註冊、事件存儲)
  2. 大體流程
  3. 具體執行過程
  4. 總結

1. 主要作兩件事

按照個人理解,react 事件註冊過程其實主要作了2件事:緩存

a. 事件註冊babel

b. 事件存儲dom

a. 事件註冊 - 組件掛載階段,根據組件內的聲明的事件類型-onclick,onchange 等,給 document 上添加事件 -addEventListener,並指定統一的事件處理程序 dispatchEvent。函數

b. 事件存儲 - 就是把 react 組件內的全部事件統一的存放到一個地方,也就是緩存起來,能夠理解成放入一個對象內,爲了在觸發事件的時候能夠查找到對應的方法去執行。this

再配個圖spa

clipboard.png

2. 大體流程

上面大體說了事件註冊須要完成的兩個目標,那完成目標的過程須要通過哪些關鍵處理呢?

首先 react 拿到將要掛載的組件的虛擬 dom(其實就是 react dom, 相似一個對象),而後處理react dom 的 props ,判斷屬性內是否有聲明爲事件的屬性,好比onclick,這個時候獲得事件類型 click 和對應的事件處理程序 fn,而後直行後面3步

a. 執行事件註冊

b. 將react dom ,事件類型,處理函數 fn 放入數組存儲

c. 組件掛載完成後,處理 b 步驟生成的數組,通過遍歷把事件處理函數存儲到listenerBank中

再配個圖

clipboard.png

3.具體執行過程

3.1 得先從 jsx 提及

看個最熟悉的代碼,也是咱們平常的寫法

handleFatherClick=()=>{

    }

    handleChildClick=()=>{

    }

    render(){
        return <div className="box">
                    <div className="father" onClick={this.handleFatherClick}>
                        <div className="child" onClick={this.handleChildClick}>child </div>
                    </div>
               </div>
    }

通過 babel 編譯後,能夠看到最終調用的方法是react.createElement,並且聲明的事件類型和回調也是一個props。

clipboard.png

react.createElement執行的結果會返回一個所謂的虛擬 dom(react element 或者 react dom),看下圖

clipboard.png

3. 2 開始處理props,拿到事件類型和回調 fn

ReactDOMComponent在進行組件加載(mountComponent)、更新(updateComponent)的時候,須要對props進行處理(_updateDOMProperties):

clipboard.png
能夠看下 registrationNameModules 的內容,就不細說了。

clipboard.png

3.3 註冊事件和事件的存儲

【註冊事件】

接着上面的代碼執行到了這個方法

enqueuePutListener(this, propKey, nextProp, transaction);

在這個方法裏會進行事件的註冊以及事件的存儲,包括冒泡和捕獲的處理

clipboard.png

根據當前的組件實例獲取獲取到最高父級-也就是document,而後執行方法 listenTo - 也是最關鍵的一個方法,進行事件綁定處理

源碼文件:ReactBrowerEventEmitter.js

clipboard.png

最後執行EventListener.listen(冒泡)或者EventListener.capture(捕獲),

單看下冒泡的註冊,其實就是addEventListener的第三個參數是 false

clipboard.png

也能夠看到註冊事件的時候也對 ie 作了兼容。

上面沒有看到 dispatchEvent 的定義,下面能夠看到傳入 dispatchEvent 方法的代碼。

clipboard.png

到這裏事件註冊就完事兒了。

【事件存儲】

下一步開始事件的存儲,在 react 裏全部事件的觸發都是經過 dispatchEvent方法統一進行派發的,而不是在註冊的時候直接註冊聲明的回調,來看下如何存儲的 。

【事件存儲結論】

react 把全部的事件和事件類型以及react 組件進行關聯,把這個關係保存在了一個 map裏,也就是一個對象裏(鍵值對),而後在事件觸發的時候去根據當前的組件id和事件類型查找到對應的事件。

再加個簡易圖

clipboard.png

看源碼:

function enqueuePutListener(inst, registrationName, listener, transaction) {

  var containerInfo = inst._hostContainerInfo;
  var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
  var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
  listenTo(registrationName, doc);//這個方法上面已說完


  //這裏涉及到了事務,事物會在之後的章節再介紹,主要看事件註冊
  //下面的代碼是將putListener放入數組,當組件掛載完後會依次執行數組的回調。也就是putListener會依次執行
  transaction.getReactMountReady().enqueue(putListener, {
    inst: inst,//組件實例
    registrationName: registrationName,//事件類型 click
    listener: listener //事件回調 fn
  });
}

function putListener() {
  var listenerToPut = this;
  //放入數組,回調隊列
  EventPluginHub.putListener(listenerToPut.inst, listenerToPut.registrationName, listenerToPut.listener);
}

大體的流程就是執行完listenTo(事件註冊),而後執行 putListener 方法進行事件存儲,全部的事件都會存儲到一個對象中 - listenerBank,具體由EventPluginHub進行管理。

//拿到組件惟一標識 id
    var getDictionaryKey = function getDictionaryKey(inst) {

      return '.' + inst._rootNodeID;

    }

   putListener: function putListener(inst, registrationName, listener) {

    //獲得組件 id
        var key = getDictionaryKey(inst);

        //獲得listenerBank對象中指定事件類型的對象
        var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {});

        //存儲回調 fn
        bankForRegistrationName[key] = listener;

        //....
  }

listenerBank其實就是一個二級 map,這樣的結構更方便事件的查找。

這裏的組件 id 就是組件的惟一標識,而後和fn 進行關聯,在觸發階段就能夠找到相關的事件回調。

看下listenerBank結構:

clipboard.png

看到這個結構是否是很熟悉呢?就是咱們日常使用的 object.

到這裏大體的流程已經說完,是否是感受有點明白又不大明白。

不要緊,再來個詳細的圖,從新理解下

clipboard.png

4.最後

本文主要是從總體流程上介紹了下 react 事件中事件的註冊過程,並無深刻到源碼的細節,有興趣的小夥兒能夠自查下源碼,也但願本文可以帶給你一些啓發,若文章有表述不清或有問題的地方歡迎留言交流。

更多精彩內容歡迎關注個人公衆號-前端張大胖

圖片描述

相關文章
相關標籤/搜索