react的事件委托机制

在开始之前,可以先看一下另一篇关于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

react16

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)触发事件

from:https://www.jianshu.com/p/b249793fd2a7

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在开始之前,可以先看一下我的另一篇关于dom本身的事件机制《谈谈js点击之后发生了什么》 前言 如果你有试过输出r...
    月肃生阅读 2,677评论 0 1
  • (1)react合成事件 react合成事件执行过程: 合成事件与原生事件的区别 1. 写法不同,合适事件是驼峰写...
    shelhuang阅读 566评论 0 0
  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,526评论 1 11
  • 以下文章为转载,对理解JavaScript中的事件处理机制很有帮助,浅显易懂,特分享于此。 什么是事件? 事件(E...
    jxyjxy阅读 3,064评论 1 10
  • 关于React事件的疑问 1.为什么要手动绑定this 2.React事件和原生事件有什么区别 3.React事件...
    React大法好阅读 1,451评论 0 2