第5节——react中的事件

一、定义事件

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

React 事件的命名采用小驼峰式(camelCase),而不是纯小写。如点击事件onClick

使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串

import React from "react";

export default class LearnEvent extends React.Component {
  /**
   * 直接在类里面定义一个方法
   */
  hello () {
    console.log("哈喽我是点击事件")
  }

  /**
   * class 中定义方法的方式有多种
   */
  hello2 = () => {

  }

  render () {
    return (
      <div>
        {/* 
          onClick后面跟上事件对应的方法名
          注意:这里要使用this从当前class中(也就是当前组件)
          获取对应的方法,所以我们类组件中绑定事件最坑的一个点就是this指向
          后面我们会着重介绍
        */}
        <button onClick={this.hello}>你好</button>
      </div>
    )
  }
}

二、this指向的问题

1、存在的问题

import React from "react";

export default class LearnEvent2 extends React.Component {
  title = "this指向的问题--es5的写法";

  add() {
    // 当前this的取决于调用的上下文环境
    console.log(this);
  }

  render() {
    /**
     *
     * 在render函数里面的this指向当前组件示例,也就是当前这个类
     * 所以我们可以直接在render里面使用this.xx 获取这个类里面对应的
     * 属性或者方法
     */
    console.log(this);
    return (
      <div>
        {/* 
          在return里面 也可以直接使用this获取当前组件的属性或者方法
        */}
        <div>{this.title}</div>

        {/* 
          这里我们可以直接用this获取到当前组件的方法
          但是 add 方法的内部this指向就会出现问题!
          add的执行上下文为Window 由于jsx经babel编译后会开启严格模式。
          所以this指向变为undefined
        */}
        <button onClick={this.add}>+1</button>
      </div>
    );
  }
}

2、使用bind改变this指向

import React from "react";

export default class LearnEvent2 extends React.Component {
  constructor () {
    super()

    /**
     * 可以再constructor里面使用this改变add的指向
     * 这种方式不方便传参 不推荐使用
     */
    this.add = this.add.bind(this)
  }

  title = "this指向的问题--es5的写法";


  add() {
    // 当前this的取决于调用的上下文环境
    console.log(this);
  }

  render() {
    /**
     *
     * 在render函数里面的this指向当前组件示例,也就是当前这个类
     * 所以我们可以直接在render里面使用this.xx 获取这个类里面对应的
     * 属性或者方法
     */
    console.log(this);
    return (
      <div>
        {/* 
          在return里面 也可以直接使用this获取当前组件的属性或者方法
        */}
        <div>{this.title}</div>

        {/* 
          这里我们可以直接用this获取到当前组件的方法
          但是 add 方法的内部this指向就会出现问题!
          add的执行上下文为Window 由于jsx经babel编译后会开启严格模式。
          所以this指向变为undefined
        */}
        <button onClick={this.add}>+1</button>
        {/* 
          我们可以通过bind函数来改变this指向
          让函数内部指向当前组件实例
          并可以还可以传递参数
        */}
        <button onClick={this.add.bind(this)}>bind +1</button>
      </div>
    );
  }
}

3、使用箭头函数绑定事件

每次render都会产生一个新的实例,对性能有影响,一般情况下可以忽略不计

import React from "react";

export default class LearnEvent3 extends React.Component {
  title = "this指向的问题--es6的写法";

  add() {
    console.log(this);
  }

  render() {
    return (
      <div>
        <div>{this.title}</div>

        {/* 
          render函数的this指向为实例自身,
          所以,我们可以直接在绑定的时候使用箭头函数,
          此时add函数内部的this指向当前上下文

          注意:使用这箭头函数这种形式要在箭头函数里面调用对应的方法
          本质上来说 事件触发的是箭头函数,然后在箭头函数里面在触发
          对应的事件方法
        */}
        <button onClick={() => this.add()}>+1</button>
      </div>
    );
  }
}

4、使用箭头函数定义实例方法

import React from "react";

export default class LearnEvent4 extends React.Component {

  /**
   * 由于add函数式使用箭头函数定义
   * 所以该函数的this 指向当前的上下文
   * 也就是指向当前实例
   * 
   * 注意:这种写法 无法进行传参,一般不推荐使用
   */
  add = () => {

  }

  render () {
    return (
      <div>
        <div>
          <button onClick={this.add}>+1</button>

          {/* 
            如果我们再事件后直接调用方法,那么每次render后都会
            执行这个方法
            这是不对的😈
          */}
          {/* <button onClick={this.add()}>+1</button> */}
        </div>
      </div>
    )
  }
}

三、合成事件

1、概念

React 合成事件(SyntheticEvent)是 React 模拟原生 DOM 事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器。它根据 W3C 规范 来定义合成事件,兼容所有浏览器,拥有与浏览器原生事件相同的接口。

// 这个onClick就是合成事件
<button onClick={xxx}>+1</button>

2、合成事件的优势

1、进行浏览器兼容,实现更好的跨平台

React 采用的是顶层事件代理机制,能够保证冒泡一致性,可以跨浏览器执行。React 提供的合成事件用来抹平不同浏览器事件对象之间的差异,将不同平台事件模拟合成事件。

2、避免垃圾回收

事件对象可能会被频繁创建和回收,因此 React 引入事件池,在事件池中获取或释放事件对象。即 React 事件对象不会被释放掉,而是存放进一个数组中,当事件触发,就从这个数组中弹出,避免频繁地去创建和销毁(垃圾回收)。

3、方便事件统一管理和事务机制

有兴趣的同学可以看一下源码的实现https://github.com/facebook/react/blob/75ab53b9e1de662121e68dabb010655943d28d11/packages/events/SyntheticEvent.js#L62

3、合成事件与原生事件区别

1、事件名称命名方式不同

原生事件命名为纯小写(onclick, onblur),而 React 事件命名采用小驼峰式(camelCase),如 onClick 等

// 原生事件绑定方式
<button οnclick="handleClick()">+1</button>
      
// React 合成事件绑定方式
const button = <button onClick={handleClick}>+1</button>
2、事件处理函数写法不同

原生事件中事件处理函数为字符串,在 React JSX 语法中,传入一个函数作为事件处理函数。

// 原生事件 事件处理函数写法
<button οnclick="handleClick()">+1</button>
      
// React 合成事件 事件处理函数写法
const button = <button onClick={handleClick}>+1</button>
3. 阻止默认行为方式不同

在原生事件中,可以通过返回 false 方式来阻止默认行为,但是在 React 中,需要显式使用 preventDefault() 方法来阻止

// 原生事件阻止默认行为方式
<a href="http://www.mobiletrain.org/" 
  οnclick="console.log('阻止原生事件'); return false"
>
  阻止原生事件
</a>

// React 事件阻止默认行为方式
const handleClick = e => {
  e.preventDefault();
  console.log('阻止原生事件~');
}
const clickElement = <a href="http://www.mobiletrain.org/" onClick={handleClick}>
  阻止原生事件
</a>

四、React 事件与原生事件执行顺序(一般了解)

import React from "react";

class LearnEvent5 extends React.Component {
  parentRef;
  childRef;
  constructor(props) {
    super(props);
    this.parentRef = React.createRef();
    this.childRef = React.createRef();
  }
  componentDidMount() {
    console.log("React componentDidMount!");

    this.parentRef.current?.addEventListener("click", () => {
      console.log("原生事件:父元素 DOM 事件监听!");
    });

    this.childRef.current?.addEventListener("click", () => {
      console.log("原生事件:子元素 DOM 事件监听!");
    });

    document.addEventListener("click", (e) => {
      console.log("原生事件:document DOM 事件监听!");
    });
    
  }
  parentClickFun = () => {
    console.log("React 事件:父元素事件监听!");
  };
  childClickFun = () => {
    console.log("React 事件:子元素事件监听!");
  };
  render() {
    return (
      <div ref={this.parentRef} onClick={this.parentClickFun}>
        <div ref={this.childRef} onClick={this.childClickFun}>
          分析事件执行顺序
        </div>
      </div>
    );
  }
}
export default LearnEvent5;

总结

1、React 所有事件都挂载在 document 对象上;

2、当真实 DOM 元素触发事件,会冒泡到 document 对象后,再处理 React 事件;

3、所以会先执行原生事件,然后处理 React 事件;

4、最后真正执行 document 上挂载的事件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值