Wsh's blog Wsh's blog
首页
  • 基础知识
  • ArkUI
  • UIAbility
  • 组件通信方式
  • 前端缓存
  • React
  • typescript
  • javascript
  • flutter
  • node
  • webpack
web3D😉
宝库📰
  • 分类
  • 标签
  • 归档
龙哥的大🐂之路 (opens new window)
GitHub (opens new window)

wsh

热爱前端的程序媛
首页
  • 基础知识
  • ArkUI
  • UIAbility
  • 组件通信方式
  • 前端缓存
  • React
  • typescript
  • javascript
  • flutter
  • node
  • webpack
web3D😉
宝库📰
  • 分类
  • 标签
  • 归档
龙哥的大🐂之路 (opens new window)
GitHub (opens new window)
  • react基础

    • react工具API
    • react组件
    • react生命周期
    • react 事件机制
    • react更新特性

    • react进阶

    • react
    • react基础
    2022-05-01
    目录

    react 事件机制

    # 01. react中的事件处理

    在语法上:

    • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
    • 使用 JSX 语法时需要传入一个函数作为事件处理函数,而不是一个字符串。
    传统:
    <button onclick="activateLasers()">
      Activate Lasers
    </button>
    React 中
     <button onClick={activateLasers}>
      Activate Lasers
    </button>
    
    • 阻止默认事件执行:不能return false 来阻止默认行为,必须通过preventDefault来阻止
    传统:
    <form onsubmit="console.log('You clicked submit.'); return false">
      <button type="submit">Submit</button>
    </form>
    
    react 中
    function Form() {
      function handleSubmit(e) {
        // e 是一个合成事件
        e.preventDefault();
        console.log('You clicked submit.');
      }
    
      return (
        <form onSubmit={handleSubmit}>
          <button type="submit">Submit</button>
        </form>
      );
    }
    

    提示

    通过 在回调中使用箭头函数绑定this,此语法问题在于每次渲染组件时都会创建不同的回调函数。如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染

    <button onClick={() => this.handleClick()}>Click me

    <button onClick={this.deleteRow.bind(this, id)}>Delete Row

    # 02. 合成事件的实现机制

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

    # 事件委派

    react的事件代理机制,不会把事件处理函数直接绑定到真实的节点上,而是把所有的事件绑定到结构的最外层,使用一个统一的事件监听器。 当组件挂载或者卸载时,只是在这个统一的事件监听器上插入或者删除一些对象;当事件发生时,首先被这个统一的事件监听器处理,然后再映射里找到真正的事件处理函数并调用。 这样做简化了事件梳理和回收机制。

    # 自动绑定

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

    在使用ES6 classes 或者纯函数的时候,这种绑定就不复存在了。需要我们手动绑定this

    • bind方法
    class Home extends React.Component {
    
      constructor(props) {
       super(props);
       this.state = {
     
       };
      }
     
      del(){
       console.log('del')
      }
     
      render() {
       return (
        <div className="home">
         <span onClick={this.del.bind(this)}>Test</span>
        </div>
       );
      }
     }
    

    提示

    如果方法只绑定,不传参,stage0 草案提供给了 - 双冒号语法。 其作用与this.del.bind(this)一致。babel已经实现了该提案。

    <span onClick={::this.del}>Test</span>
    
    • 构造器内声明 在组件的构造函数内完成了this的绑定,这种绑定方法的好处在于仅需要进行一次绑定,而不需要每次调用事件监听器时去执行绑定操作。
      constructor(props) {
       super(props);
       this.del = this.del.bind(this)
      }
    
    • 箭头函数 箭头函数不仅仅是函数的语法糖,它还自动绑定了定义此函数作用域的this, 因此我们不需要对它进行bind操作。 写法一:
     del = () => {
        console.log('del')
      }
      render() {
        return (
          <div className="home">
            <span onClick={this.del}>11111</span>
          </div>
        );
      }
    

    写法二:

      del(){
      }
      render() {
        return (
          <div className="home">
            <span onClick={() => this.del()}>11111</span>
          </div>
        );
      }
    

    # 03. 合成事件与原生事件混用

    应该尽量避免在React中混用合成事件和原生DOM事件。另外,用reactEvent.nativeEvent.stopPropagation() 来阻止冒泡时不行的。阻止React事件冒泡的行为只能用于React合成系统中,且没办法阻止原生事件的冒泡。反之,在原生事件中的阻止冒泡行为,可以阻止React合成事件的传播。

    React的合成事件系统只是原生DOM事件系统的一个子集。它仅仅实现了DOM Level3的事件接口,并且统一了浏览器的兼容性问题。 有些事件React没有实现,或者说受限制,没有办法去实现。比如window的resize事件。

    对于无法使用React的合成事件的场景,我们需要使用原生事件来完成。

    # 04. 合成事件/js原生事件对比

    主要从四个方面对比

    • 事件传播与阻止事件传播 浏览器原生DOM事件的传播可以分为3个阶段:事件捕获,目标对象本身的事件处理程序调用,事件冒泡。
    1. 事件捕获会优先调用结构树最外层的元素上绑定的事件监听器
    2. 然后依次向内调用,一直调用到目标元素上的事件监听器为止。(可以在e.addEventListener()的第三个参数设置为true时,为元素e注册捕获事件处理程序,并且在事件传播的第一个阶段调用)
    • 事件冒泡与事件捕获相反,从目标元素向外传播,由内向外直到最外层。

    阻止原生事件传播需要使用e.stopPropagation(),不过对于该方法的浏览器,需要使用.cancalBubble = true来阻止。在react 合成事件中,只需要使用stopProgapation()

    • 事件类型 React 合成事件的事件类型是js原生事件类型的一个子集。
    • 事件绑定方式 原生绑定方式有多种,而React的合成事件的绑定方式简单
    • 事件对象 原生DOM事件对象在W3C标准和IE标准下存在差异。在低版本的IE浏览器中,使用window.event 来获取事件对象。而在React合成事件系统中,不存在这种兼容性问题,在事件处理函数中可以得到一恶搞合成事件对象。

    # 05. react 事件机制特点

    由于fiber机制的特点,生成一个fiber节点时,它对应的dom节点有可能还未挂载,onClick这样的事件处理函数作为fiber节点的prop,不能直接被绑定到真实的DOM节点上。所以React提供了一种“顶层注册,事件收集,统一触发”的事件机制。利用fiber树的层级关系来生成事件执行路径,进而模拟事件捕获和冒泡

    • 顶层注册: 在root元素上绑定一个统一的事件处理函数
    • 事件收集:事件触发时(实际上是root上的事件处理函数被执行),构造合成事件对象,按照冒泡或捕获的路径去组件中收集真正的事件处理函数
    • 统一触发: 在收集过程之后,对所收集的事件逐一执行,并共享同一个合成事件对象。

    # 03. react事件注册机制

    • 事件注册 - 组件挂载阶段,根据组件内的声明的事件类型-onclick,onchange 等,给 root(17之前是添加到document) 上添加事件 -addEventListener,将事件绑定到root元素上

    事件监听器listener:是执行 createEventListenerWrapperWithPriority的调用结果,根据优先级和事件名的映射关系返回不同的事件。

    • 事件存储 - 就是把 react 组件内的所有事件统一的存放到一个对象里,缓存起来,为了在触发事件的时候可以查找到对应的方法去执行。

    # 04. react事件合成机制

    合成事件对象: 经过React合成的SyntheticEvent对象

    1. 对原生事件的封装
    2. 对某些原生事件的升级和改造
    3. 不同浏览器事件兼容的处理
    4. 通过将事件 normalize 以让他们在不同浏览器中拥有一致的属性。

    如果一个节点上同时绑定了合成和原生事件,那么禁止冒泡后执行关系是怎样的?

    因为合成事件的触发是基于浏览器的事件机制来实现的,通过冒泡机制冒泡到最顶层元素,然后再由 dispatchEvent统一去处理。

    • 原生事件阻止冒泡肯定会阻止合成事件的触发。
    • 合成事件的阻止冒泡不会影响原生事件。 浏览器事件的执行需要经过三个阶段,捕获阶段-目标元素阶段-冒泡阶段。

    节点上的原生事件的执行是在目标阶段,合成事件的执行是在冒泡阶段,所以原生事件会先合成事件执行,然后再往父节点冒泡。

    事件执行路径:当事件对象合成完毕,将事件收集到事件执行路径上,从触发事件的元素开始,依据fiber树的层级结构向上查找,累加上级元素中所有相同类型的事件,最终形成一个具有所有相同类型事件的数组。React自己模拟了一套事件捕获与冒泡的机制。

    总结: 由于fiber树的特点,一个组件如果含有事件的prop,那么将会在对应fiber节点的commit阶段绑定一个事件监听到root上,这个事件监听是持有优先级的,将它和优先级机制联系了起来,可以把合成事件机制当作一个协调者,负责去协调合成事件对象、收集事件、触发真正的事件处理函数这三个过程

    #react
    react生命周期
    react18 更新特性

    ← react生命周期 react18 更新特性→

    最近更新
    01
    组件通信方式
    01-07
    02
    UIAbility
    01-07
    03
    ATKTS
    01-06
    更多文章>
    Theme by Vdoing | Copyright © 2022-2025 Wsh | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式