react合成事件与原生事件的相关理解

 更新时间:2021年05月13日 17:13:47   作者:铸心  
本文主要介绍了react合成事件与原生事件的相关概念,帮助大家区分这两种事件,学习react的同学不妨了解下

1. 原生事件

原生事件就是js的原生事件,如通过document.addEventListener来设置的监听事件。

在react中即使有自己的一套事件机制(见下面合成事件),但有时候的业务场景我们仍然需要使用原生事件。比如我们封装一个Modal弹窗组件,需要在点击非弹窗区域时关掉弹窗,此时我们只能针对document进行原生点击事件监听。

由于原生事件需要绑定在真实DOM上,所以一般是在componentDidMount阶段或者组件/元素的ref的函数执行阶段进行绑定操作,并且注意要在componentWillUnmount阶段进行解绑操作以避免内存泄漏。

2. 合成事件

React有自己的一套事件机制,它重新封装了绝大部分的原生事件。合成事件采用了事件池,这样做可以大大节省内存,而不会频繁的创建和销毁事件对象。

在React中,如果需要绑定事件,我们常常在jsx中这么写:

handleClick(){
}
<div onClick={this.handleClick.bind(this)}>
	react事件
</div>

大致原理:

React并不是将click事件绑在该div的真实DOM上,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装并交由真正的处理函数运行。

以上面的代码为例,整个事件生命周期示意如下:

合成事件的一些特点总结:

  • React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
  • React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
  • React 通过对象池的形式管理合成事件对象的创建和销毁,减少了垃圾的生成和新对象内存的分配,提高了性能

了解react合成事件的大概原理后,方便我们解答下面一个问题:

为什么react事件需要手动绑定this

合成事件触发之后会冒泡一路到document的节点,然后开始分发document节点收集到的事件,这个时候react从事件触发的组件实例开始, 遍历虚拟dom树,从树上取下我们绑定的事件,收集起来,然后执行。举个例子:

class Test extends React.Component {
   fatherHandler =  function father() { /*...*/}
   childHander = function child() {/*...*/}

   render(){
     return (
       <div onClick={this.fatherHandler}>
         <span onClick={this.childHander}>
         </span>
       </div>
     );
   }

}

当事件触发以后react会把上面的事件处理函数放到一个数组里是这样的

[father, child]

最后,react只要遍历执行这个数组,就能执行所有需要执行的事件处理函数。这里react对函数进行了临时保存,这个时候执行的话,this自然就丢失了。

如果react保存顺便保存一下实例,还是可以做到,不需要你绑定this的,但是这样对于react来说代价太大了。

3. 原生与合成事件触发顺序

  componentDidMount() {
    this.parent.addEventListener('click', (e) => {
      console.log('dom parent');
    })
    this.child.addEventListener('click', (e) => {
      console.log('dom child');
    })
    document.addEventListener('click', (e) => {
      console.log('document');
    })
  }

  childClick = (e) => {
    console.log('react child');
  }

  parentClick = (e) => {
    console.log('react parent');
  }

  render() {
    return (
      <div onClick={this.parentClick} ref={ref => this.parent = ref}>
        <div onClick={this.childClick} ref={ref => this.child = ref}>
          test
        </div>
      </div>)
  }


点击child中的test后,事件触发顺序如下:

结论:

无论是否是对于同一元素监听的同种类型事件,原生事件总是比合成事件先触发。这是由于上面我们说到的合成事件最终都会绑定到documnet DOM上导致的,当合成事件监听到后,总是冒泡到document才会真正触发。 而documnet DOM上监听的原生事件则总是最后触发

4. 合成事件和原生事件混用

react合成事件和原生事件最好不要混用。

原生事件中如果执行了stopPropagation(阻止冒泡)方法,则很容易导致其他同类型react合成事件失效。因为这样所有同级以及后代元素的合成事件和原生事件都将无法冒泡到document上。

而如果仅仅是合成事件中使用了e.stopPropagation(阻止冒泡)方法,则不会影响原生事件的冒泡

相关疑问:

我们知道React事件监听器中获得的入参并不是浏览器原生事件,原生事件可以通过e.nativeEvent来获取。通过这种方式,合成事件可以影响原生事件吗?

e.nativeEvent.stopPropagation

即使在react的合成事件中调用原生事件的阻止冒泡,实际作用是在DOM最外层阻止冒泡,并不符合预期。也就是说它最终只能控制当前监听的合成事件不会冒泡到document DOM的原生事件

e.nativeEvent.stopImmediatePropagation

该方法与上面的nativeEvent.stopPropagation有类似的功能,都可阻止当前监听的合成事件冒泡到document DOM的原生事件

stopImmediatePropagation常常在多个第三方库混用时,用来阻止多个事件监听器中的非必要执行。比如同一个元素的同种事件,设置了多个监听事件函数,则该方式可以控制监听函数只触发第一个

stopImmediatePropagation和stopPropagation本都是原生事件,但在React自己的事件体系中,重新封装了后者,却没有封装前者。导致在合成事件中只能手动调用nativeEvent.stopImmediatePropagation。

因为在React的合成事件机制中,一个组件只能绑定一个同类型的事件监听器(重复定义时,后面的监听器会覆盖之前的),所以合成事件无需去封装stopImmediatePropagation。

所以,在React的合成事件中,e.nativeEvent.stopPropagation和e.nativeEvent.stopImmediatePropagation实际的作用是等价的

此外,由于事件绑定的顺序问题,需要注意,如果是在react-dom.js加载前绑定的document原生事件,stopImmediatePropagation也是无法阻止的。

以上就是react合成事件与原生事件的相关理解的详细内容,更多关于react合成事件与原生事件的资料请关注脚本之家其它相关文章!

相关文章

  • react-native DatePicker日期选择组件的实现代码

    react-native DatePicker日期选择组件的实现代码

    本篇文章主要介绍了react-native DatePicker日期选择组件的实现代码,具有一定的参考价值,有兴趣的可以了解下
    2017-09-09
  • 浅谈react-router@4.0 使用方法和源码分析

    浅谈react-router@4.0 使用方法和源码分析

    这篇文章主要介绍了浅谈react-router@4.0 使用方法和源码分析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-06-06
  • React事件处理的机制及原理

    React事件处理的机制及原理

    这篇文章主要介绍了React事件处理的机制及原理,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • React学习之事件绑定的几种方法对比

    React学习之事件绑定的几种方法对比

    这篇文章主要给大家介绍了关于React学习之事件绑定的几种方法对比,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-09-09
  • react antd实现动态增减表单

    react antd实现动态增减表单

    antd是react流行的ui框架库,本文主要介绍了react antd实现动态增减表单,分享给大家,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • 详解在React中跨组件分发状态的三种方法

    详解在React中跨组件分发状态的三种方法

    这篇文章主要介绍了详解在React中跨组件分发状态的三种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • React合成事件详解

    React合成事件详解

    这篇文章主要介绍了React合成事件的相关资料,帮助大家更好的理解和学习使用React,感兴趣的朋友可以了解下
    2021-05-05
  • 详解React Native 屏幕适配(炒鸡简单的方法)

    详解React Native 屏幕适配(炒鸡简单的方法)

    React Native 可以开发 ios 和 android 的 app,在开发过程中,势必会遇上屏幕适配,这篇文章主要介绍了详解React Native 屏幕适配(炒鸡简单的方法),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • react中使用css的7中方式(最全总结)

    react中使用css的7中方式(最全总结)

    这篇文章主要介绍了react中使用css的7中方式(最全总结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02
  • 详解基于webpack搭建react运行环境

    详解基于webpack搭建react运行环境

    本篇文章主要介绍了详解基于webpack搭建react运行环境,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06

最新评论