美文网首页
react源码分析(3):react的事件委托机制

react源码分析(3):react的事件委托机制

作者: 月肃生 | 来源:发表于2019-05-21 09:42 被阅读0次

    在开始之前,可以先看一下我的另一篇关于dom本身的事件机制《谈谈js点击之后发生了什么》

    前言

        如果你有试过输出react事件中的event,你就会发现这个event好像和我们看到的dom事件中的event不太一样,那是因为react在进行dom事件绑定时,不是直接绑定事件的,而是通过所谓的合成事件(SyntheticEvent)进行委托管理的,它是原生事件进行封装后的结果,你可以通过nativeEvent获取原生事件。

    通过例子观察

    class App extends Component {
      componentDidMount(){
        document.addEventListener('click', function(){
          console.log('document click')
        })
        document.getElementsByClassName('App')[0].addEventListener('click', function(){
          console.log('app click')
        })
        document.getElementsByTagName('button')[0].addEventListener('click', function(e){
          console.log('button click')
          // e.stopPropagation();
        })
      } 
      onClick = (e) => {
        e.stopPropagation() // 能够阻止div.app的触发
        e.nativeEvent.stopImmediatePropagation(); // 能够阻止document的触发
        e.nativeEvent.stopPropagation(); // 什么都阻止不了
        console.log('react button click');
      };
      render() {
        return (
          <div className="App" onClick={() => {console.log('react app click')}}>
            <button onClick={this.onClick}>按钮</button>
          </div>
        );
      }
    }
    

    我们在用react绑定了两个事件,同时在didmount给真实dom也绑定了事件,点击button之后的执行顺序是

    button click
    app click
    react button click
    react app click
    document click
    

    原理

    react并不是我之前所设想的将事件绑定在真实dom上,而是通过自己的事件处理器来处理,将所有的事件都绑定在document上,这样真实点击的时候,冒泡到document上,react再通过document去dispatchEvent统一处理事件

    所以上面的stopPropagation就能理解了,e.stopPropagation只能阻止虚拟dom的事件冒泡,但它本身是由document触发的,所以e.nativeEvent.stopPropagation什么也阻止不了,document就是冒泡的顶点,e.nativeEvent.stopImmediatePropagation可以阻止document的事件,这和它本身有关

    需要注意的是react在生成真实的dom节点会加入一些东西帮助事件分发
    dom节点的react属性(15和16有点区别)

    react15.png react16.png

    15和16会有一点细微的区别,具体什么还没有去了解,但是看来对事件的影响不大,15是有一个__rootNodeID来区分组件,16是通过__debugID来区分,如果理解上有错误,欢迎指正哦

    • vdom
    // 简化
    let vdom = {
      type: 'div',
      props: {
        onClick: function(){
          console.log('react app click')    
        },
        children: [
          {
            type: 'button',
            props: {
               onClick: function() {
                  console.log('react button click')    
               }
            }
          }
        ]
      }
    }
    
    • 注册事件
    let bankForRegistrationName = {}; // 回调事件的保存
    // react构建真实dom树
    ...
    // 注册事件
    bankForRegistrationName = {
        // 数字是_debugID,react用于识别每一个dom
        5: {
          click: function(){
            console.log('react app click')    
          },
        },
        6: {
          click: function(){
            console.log('react button click')    
          },
        }
    }
    
    • 事件触发
    // 合成事件简单实现
    function SyntheticEvent(e) {
      ...
      this.nativeEvent = e;
      ...
    }
    // e: event, type: 事件类型
    function dispatchEvent(e, type) {
      let synE = new SyntheticEvent(e);
      // 执行监听事件
      let debugID = e.target.__reactInternalInstance$om8tco7dvl._debugID;
      bankForRegistrationName[debugID][type](synE); 
    }
    // document事件委托
    document.addEventListener('click', function(e) {
      dispatchEvent(e, 'click');
    })
    

    总结

        基本上就是这样了(当然没有对冒泡做处理,react会遍历自己的vdom去执行冒泡)

    1. 事件管理中心(bankForRegistrationName)会在react-render过程中保存所有所有dom事件
    2. document作为事件委托者,用来分发事件(dispatchEvent),通过dom节点唯一标识(_debugID)去事件管理(bankForRegistrationName)触发事件

    相关文章

      网友评论

          本文标题:react源码分析(3):react的事件委托机制

          本文链接:https://www.haomeiwen.com/subject/wmenaqtx.html