React的事件系统

18 篇文章 1 订阅


React基于Virtual DOM实现了一个合成事件层,我们所定义的时间处理器会接受到一个合成事件对象的实例,它完全符合W3C标准,因此不会存在兼容性问题。

同样支持事件的冒泡机制,所有的事件都自动绑定到最外层上(document)。如果需要访问原生事件对象,可以使用nativeEvent对象

合成事件的绑定方式

在DOM0级事件中,事件处理器是直接绑定到HTML元素之上,例如:

<div onclick="clickHandler()">Click</div>

而React借鉴了这种写法:

<button onClick={this.clickHandle}>Click</button>

但也仅仅是写法相近,在JSX中需要使用驼峰命名法的形式来书写事件的属性名,例如上面的onClick,此外在HTML中,属性值只能是字符串,而在JSX中可以是任意类型。

合成事件的实现机制

在React底层主要对合成事件做了两件事:事件委派和自动绑定

事件委托

在React中,事件处理函数并不是直接绑定到真正的节点上,而是把所有事件绑定到结构的最外层。使用一个统一的事件监听器,这个事件监听器维持了一个映射来保存所有组件内部的事件监听和处理函数。

当事件发生时首先被这个统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用。

自动绑定

在React组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前组件,而且React还会对这种引用进行缓存。

但是在用ES6的class写法或纯函数写法时,这种自动绑定就不存在了,需要手动绑定。

  • bind方法:
import React, {Component} from 'react';

class App extends Component{
  constructor(props){
    super(props);
  }

  clickHandle(e){
    console.log(e);
  }

  render(){
    return (
      <div ref="div">
        <button onClick={this.clickHandle.bind(this)}>Click Me</button>
      </div>
    )
  }
}

如果方法只绑定,不传参,那还有一个更便捷的方法–双冒号语法。即:

import React, {Component} from 'react';

class App extends Component{
  constructor(props){
    super(props);
  }

  clickHandle(e){
    console.log(e);
  }

  render(){
    return (
      <div ref="div">
        <button onClick={::this.clickHandle}>Click Me</button>
      </div>
    )
  }
}
  • 构造器声明:
import React, {Component} from 'react';

class App extends Component{
  constructor(props){
    super(props);
    this.clickHandle = this.clickHandle.bind(this);
  }

  clickHandle(e){
    console.log(e);
  }

  render(){
    return (
      <div ref="div">
        <button onClick={this.clickHandle}>Click Me</button>
      </div>
    )
  }
}

这样写的好处在于仅需要进行一次绑定,而不需要每次调用事件处理函数的时候都执行一次绑定操作。

在React中使用原生事件

除了合成事件,在React中也可以使用原生事件,在componentDidMount生命周期中,组件已经完成挂载并且在浏览器中存在真实的DOM。

import React, {Component} from 'react';
import './App.css';

class App extends Component{
  constructor(props){
    super(props);
    this.clickHandle = this.clickHandle.bind(this);
  }

  clickHandle(e){
    console.log('子元素');
  }

  componentDidMount(){
    const div = this.refs.div;
    div.addEventListener('click', (e)=>{
      if(e.target.tagName === 'BUTTON'){
        console.log('不执行操作');
        return;
      }
    });
  }

  componentWillUnmount(){
    const div = this.refs.div;
    div.removeEventListener('click');
  }

  render(){
    return (
      <div ref="div">
        <button onClick={this.clickHandle}>Click Me</button>
      </div>
    )
  }
}

使用DOM原生事件时,一定要在组件卸载时手动移除,否则可能出现内存泄漏的问题。

原生事件和合成事件的混用

import React, {Component} from 'react';
import './App.css';

class App extends Component{
  constructor(props){
    super(props);
    this.state = {
      inputState: ''
    }
    this.clickHandle = this.clickHandle.bind(this);
  }

  clickHandle(e){
    console.log('子元素');
  }

  componentDidMount(){
    const div = this.refs.div;
    div.addEventListener('click', (e)=>{
      console.log('父级元素');
    });
  }

  componentWillUnmount(){
    const div = this.refs.div;
    div.removeEventListener('click');
  }

  render(){
    // const {inputState} = this.state;
    return (
      <div ref="div">
        <button onClick={this.clickHandle}>Click Me</button>
      </div>
    )
  }
}

点击按钮之后,控制台输出结果如下:

[外链图片转存失败(img-I6wKjpak-1562825830740)(http://note.youdao.com/yws/res/16726/012ABB624C2D457786AD40B52265F3F4)]

可以看到,子元素和父元素的事件处理函数都被触发了。一般来说,我们会在子元素的事件处理函数内执行stopPropagation来阻止事件的冒泡。不过这在React中是不起作用的

首先,对于合成事件来说,stopPropagation只能阻止合成事件的冒泡,而不能阻止原生事件。

可能我们马上会想到调用合成事件对象的nativeEventstopPropagation不就可以阻止原生事件的冒泡了吗?的确,理论上应该是可以的,然而因为React将合成事件的事件处理函数绑定在了最外层(document)上,所以等到最外层再执行也已是为时已晚。

对于这种情况,应该通过判别事件的target对象来避免。

class App extends Component{
  constructor(props){
    super(props);
    this.state = {
      inputState: ''
    }
    this.clickHandle = this.clickHandle.bind(this);
  }

  clickHandle(e){
    console.log('子元素');
  }

  componentDidMount(){
    const div = this.refs.div;
    div.addEventListener('click', (e)=>{
      if(e.target.tagName === 'BUTTON'){
        console.log('不执行操作');
        return;
      }
    });
  }

  componentWillUnmount(){
    const div = this.refs.div;
    div.removeEventListener('click');
  }

  render(){
    return (
      <div ref="div">
        <button onClick={this.clickHandle}>Click Me</button>
      </div>
    )
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React事件委托是指在React中,通过将事件监听器添加到父组件上,从而实现对子组件的事件处理。React使用事件委托的方式来提高性能和代码复用。通过事件委托,可以在父组件上监听特定类型的事件,并在合适的时候将事件传递给对应的子组件进行处理。 在React中,事件委托的实现主要依靠React事件系统和组件的虚拟DOM机制。React使用了一种称为合成事件(SyntheticEvent)的机制来处理事件。当父组件监听到一个特定类型的事件时,React会将这个事件转化为一个合成事件对象,并将这个对象传递给父组件的事件处理函数。然后父组件可以根据合成事件对象的信息,判断事件源是哪个子组件,并调用相应的子组件的事件处理函数进行处理。 引用和是关于React事件委托的源码文件。其中,ReactBrowserEventEmitter.js定义了React事件委托的核心逻辑,包括事件的注册、分发等功能。而引用的ReactDOMEventListener.js则负责将DOM事件转化为合成事件。 综上所述,React事件委托是通过将事件监听器添加到父组件上,利用React事件系统和虚拟DOM机制,实现对子组件的事件处理和分发。这种机制可以提高性能和代码复用,并且方便管理事件逻辑。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [jquery 监听td点击事件_React 事件 | 1. React 中的事件委托](https://blog.csdn.net/weixin_39538789/article/details/110105355)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值