首页 > 其他分享 >React - 10 react中的合成事件

React - 10 react中的合成事件

时间:2023-06-24 22:31:52浏览次数:48  
标签:10 react log 绑定 React 事件 console 冒泡

1.react中的合成事件

推荐使用箭头函数,不用管this,但是如果要传参,还得通过bind,事件对象永远是最后一个参数

import React from "react";

class Demo extends React.Component {
    /* 
    基于React内部的处理,如果我们给合成事件绑定一个“普通函数”,当事件行为触发,绑定的函数执行;方法中的this会是undefined「不好」!! 解决方案:this->实例
      + 我们可以基于JS中的bind方法:预先处理函数中的this和实参的
      + 推荐:当然也可以把绑定的函数设置为“箭头函数”,让其使用上下文中的this「也就是我们的实例」

    合成事件对象SyntheticBaseEvent:我们在React合成事件触发的时候,也可以获取到事件对象,只不过此对象是合成事件对象「React内部经过特殊处理,把各个浏览器的事件对象统一化后,构建的一个事件对象」
      合成事件对象中,也包含了浏览器内置事件对象中的一些属性和方法「常用的基本都有」
      + clientX/clientY
      + pageX/pageY
      + target
      + type
      + preventDefault
      + stopPropagation
      + ...
      + nativeEvent:基于这个属性,可以获取浏览器内置『原生』的事件对象
      + ...
    */
    handle1() { //Demo.prototype => Demo.prototype.handle=function handle(){}
        console.log(this); //undefined
    }
    handle2(x, y, ev) {
        // 只要方法经过bind处理了,那么最后一个实参,就是传递的合成事件对象!!
        console.log(this, x, y, ev); //实例 10 20 合成事件对象
    }
    handle3 = (ev) => {  //实例.handle3=()=>{....}
        console.log(this); //实例
        console.log(ev); //SyntheticBaseEvent 合成事件对象「React内部经过特殊处理,把各个浏览器的事件对象统一化后,构建的一个事件对象」
    };
    handle4 = (x, ev) => {
        console.log(x, ev); //10 合成事件对象
    };

    render() {
        /*
         bind在React事件绑定的中运用
           + 绑定的方法是一个普通函数,需要改变函数中的this是实例,此时需要用到bind「一般都是绑定箭头函数」
           + 想给函数传递指定的实参,可以基于bind预先处理「bind会把事件对象以最后一个实参传递给函数」 
         */
        return <div>
            <button onClick={this.handle1}>按钮1</button>
            <button onClick={this.handle2.bind(this, 10, 20)}>按钮2</button>
            <button onClick={this.handle3}>按钮3</button>
            <button onClick={this.handle4.bind(null, 10)}>按钮4</button>
        </div>;
    }
}

export default Demo;

2. 事件及事件委托

React - 10 react中的合成事件_react

1 捕获、2目标、3冒泡

React - 10 react中的合成事件_react_02

使用事件委托以后,如果某个事件直接绑定到指定元素了不需要委托,那么直接在该元素的事件中阻止冒泡即可event.stopPropagation()

/* 
        事件委托:利用事件的传播机制,实现的一套事件绑定处理方案 
          例如:一个容器中,有很多元素都要在点击的时候做一些事情
            传统方案:首先获取需要操作的元素,然后逐一做事件绑定
            事件委托:只需要给容器做一个事件绑定「点击内部的任何元素,根据事件的冒泡传播机制,都会让容器的点击事件也触发;我们在这里,根据事件源,做不同的事情就可以了;」
          优势:
            + 提高JS代码运行的性能,并且把处理的逻辑都集中在一起!!
            + 某些需求必须基于事件委托处理,例如:除了点击xxx外,点击其余的任何东西,都咋咋咋...
            + 给动态绑定的元素做事件绑定
            + ...
          限制:
            + 当前操作的事件必须支持冒泡传播机制才可以
              例如:mouseenter/mouseleave等事件是没有冒泡传播机制的
            + 如果单独做的事件绑定中,做了事件传播机制的阻止,那么事件委托中的操作也不会生效!!
        */
        const body = document.body;
        body.addEventListener('click', function (event) {
            // event.target:事件源「点击的是谁,谁就是事件源」
            let target = event.target,
                id = target.id;
            if (id === "root") {
                console.log('root');
                return;
            }
            if (id === "inner") {
                console.log('inner');
                return;
            }
            if (id === "AAA") {
                console.log('AAA');
                return;
            }
            // 如果以上都不是,我们处理啥....
        });
        const outer = document.querySelector('#outer');
        outer.addEventListener('click', function (event) {
            console.log('outer');
            event.stopPropagation();
        });

3.react事件委托原理

/* 
 React中合成事件的处理原理
   “绝对不是”给当前元素基于addEventListener单独做的事件绑定,React中的合成事件,都是基于“事件委托”处理的!
     + 在React17及以后版本,都是委托给#root这个容器「捕获和冒泡都做了委托」;
     + 在17版本以前,都是为委托给document容器的「而且只做了冒泡阶段的委托」;
     + 对于没有实现事件传播机制的事件,才是单独做的事件绑定「例如:onMouseEnter/onMouseLeave...」

   在组件渲染的时候,如果发现JSX元素属性中有 onXxx/onXxxCapture 这样的属性,不会给当前元素直接做事件绑定,只是把绑定的方法赋值给元素的相关属性!!例如:
     outer.onClick=() => {console.log('outer 冒泡「合成」');}  //这不是DOM0级事件绑定「这样的才是 outer.onclick」
     outer.onClickCapture=() => {console.log('outer 捕获「合成」');}
     inner.onClick=() => {console.log('inner 冒泡「合成」');}
     inner.onClickCapture=() => {console.log('inner 捕获「合成」');}

   然后对#root这个容器做了事件绑定「捕获和冒泡都做了」
     原因:因为组件中所渲染的内容,最后都会插入到#root容器中,这样点击页面中任何一个元素,最后都会把#root的点击行为触发!!
     而在给#root绑定的方法中,把之前给元素设置的onXxx/onXxxCapture属性,在相应的阶段执行!!
*/
import React, { Component, PureComponent} from 'react'
import PropTypes from 'prop-types'


class Demo extends React.Component {
  render () {
    return <div className="outer"
      onClick={
        () => {
          console.log('outer 冒泡 -- 合成')
        }
      }
      onClickCapture={
        () => {
          console.log('outer 捕获 -- 合成')
        }
      }
    >
     <div className="inner"
     onClick={
      () => {
        console.log('inner 冒泡 -- 合成')
      }
     }
     onClickCapture={
      () => {
        console.log('inner 捕获 -- 合成')
      }
     }
     ></div>
    </div>
  }
  componentDidMount () {
    document.addEventListener('click', () => {
      console.log('document 捕获');
    }, true)
    document.addEventListener('click', () => {
      console.log('document 冒泡');
    }, false)
    document.body.addEventListener('click', () => {
      console.log('body 捕获');
    }, true)
    document.body.addEventListener('click', () => {
      console.log('body 冒泡');
    }, false)

    let root = document.querySelector('#root')
    root.addEventListener('click', () => {
      console.log('root 捕获');
    }, true)
    root.addEventListener('click', () => {
      console.log('root 冒泡');
    }, false)

    let outer = document.querySelector('.outer')
    outer.addEventListener('click', () => {
      console.log('outer 捕获 -- 原生');
    }, true)
    outer.addEventListener('click', () => {
      console.log('outer 冒泡 -- 原生');
    }, false)

    let inner = document.querySelector('.inner')
    inner.addEventListener('click', () => {
      console.log('inner 捕获 -- 原生');
    }, true)
    inner.addEventListener('click', () => {
      console.log('inner 冒泡 -- 原生');
    }, false)
  }
}

在页面中点击inner后的顺序

React - 10 react中的合成事件_react_03

合成事件的原理

React - 10 react中的合成事件_react_04

React - 10 react中的合成事件_react_05

React - 10 react中的合成事件_react_06

React - 10 react中的合成事件_react_07

react16中的事件委托触发顺序

React中合成事件的处理原理
    在16版本中,合成事件的处理机制,不再是把事件委托给#root元素,而是委托给document元素,并且只做了冒泡阶段的委托;在委托的方法中,把onXxx/onXxxCapture合成事件属性进行执行!!

 React16中,关于合成事件对象的处理,React内部是基于“事件对象池”,做了一个缓存机制!!React17及以后,是去掉了这套事件对象池和缓存机制的!!
   + 当每一次事件触发的时候,如果传播到了委托的元素上「document/#root」,在委托的方法中,我们首先会对内置事件对象做统一处理,生成合成事件对象!!
     在React16版本中:
     为了防止每一次都是重新创建出新的合成事件对象,它设置了一个事件对象池「缓存池」
       + 本次事件触发,获取到事件操作的相关信息后,我们从 事件对象池 中获取存储的合成事件对象,把信息赋值给相关的成员!
       + 等待本次操作结束,把合成事件对象中的成员信息都清空掉,再放入到 事件对象池 中!!

React - 10 react中的合成事件_react_08

React - 10 react中的合成事件_react_09

原生执行完才执行合成事件

React - 10 react中的合成事件_react_10

React - 10 react中的合成事件_react_11

4.解决移动端的点击事件300ms延迟

import React from "react";

class Demo extends React.Component {

    // 手指按下:记录手指的起始坐标
    touchstart = (ev) => {
        let finger = ev.changedTouches[0]; //记录了操作手指的相关信息
        this.touch = {
            startX: finger.pageX,
            startY: finger.pageY,
            isMove: false
        };
    };
    // 手指移动:记录手指偏移值,和误差值做对比,分析出是否发生移动
    touchmove = (ev) => {
        let finger = ev.changedTouches[0],
            { startX, startY } = this.touch;
        let changeX = finger.pageX - startX,
            changeY = finger.pageY - startY;
        if (Math.abs(changeX) > 10 || Math.abs(changeY) > 10) {
            this.touch.isMove = true;
        }
    };
    // 手指离开:根据isMove判断是否是点击
    touchend = () => {
        let { isMove } = this.touch;
        if (isMove) return;
        // 说明触发了点击操作
        console.log('点击了按钮');
    };

    render() {
        return <div>
            <button onTouchStart={this.touchstart}
                onTouchMove={this.touchmove}
                onTouchEnd={this.touchend}>
                提交
            </button>
        </div>;
    }
}

export default Demo;

放在业务组件下面

React - 10 react中的合成事件_react_12

循环创建的元素循环绑定事件 --> 在react中没问题,因为react本身就是通过委托事件来实现的

import React from "react";

class Demo extends React.Component {
    state = {
        arr: [{
            id: 1,
            title: '新闻'
        }, {
            id: 2,
            title: '体育'
        }, {
            id: 3,
            title: '电影'
        }]
    };

    handle = (item) => {
        // item:点击这一项的数据
        console.log('我点击的是:' + item.title);
    };

    render() {
        let { arr } = this.state;
        return <div>
            {arr.map(item => {
                let { id, title } = item;
                return <span key={id}
                    style={{
                        padding: '5px 15px',
                        marginRight: 10,
                        border: '1px solid #DDD',
                        cursor: 'pointer'
                    }}
                    onClick={this.handle.bind(this, item)}>
                    {title}
                </span>;
            })}
        </div>;
    }
}

export default Demo;



标签:10,react,log,绑定,React,事件,console,冒泡
From: https://blog.51cto.com/u_12207234/6542062

相关文章

  • 10. Spring整合
    课程学习到这里,已经对Spring有一个简单的认识了,Spring有一个容器,叫做IoC容器,里面保存bean。在进行企业级开发的时候,其实除了将自己写的类让Spring管理之外,还有一部分重要的工作就是使用第三方的技术。前面已经讲了如何管理第三方bean了,下面结合IoC和DI,整合2个常用......
  • Android开发社招10个公司28轮面试面经(含字节、拼多多、美团、滴滴......)
    个人情况学历:二本工作年限:2年半面试结果:拿到了字节、拼多多、美团、滴滴、欧科云链…的offer面经面经奉上,不是很全,希望有帮助字节项目认识的汇编代码类继承中父类和子类的内存布局讲一讲虚拟内存空间线程池中线程数量如何设计信号量机制子类中变量初始化顺序和销毁顺序线程调度算......
  • 分享我的 Shell 环境,git 操作效率提升 100% !
    换到一个新的开发环境,蛮多东西要折腾的。特地整理了一下,下次换新电脑也方便。git:不使用rebase,要加上这个设置:gitconfig--globalpull.rebasefalse全局配置工作用户名和邮箱,不然会影响到你提交代码:gitconfig--globaluser.name"???"gitconfig--globaluser.email......
  • 初学编程100个代码
    Java、Python等主流编程语言如今火的不行,初学编程都有哪100个代码呢?笔者结合实际开发经验和同学们最迫切关注的技术热点,总结了100个常用的代码实现,具体如下:1.输出"Hello,World!":print("Hello,World!")2.求两个数的和:点击查看代码a=5b=7c=a+bpri......
  • TensorFlow10.4 卷积神经网络-batchnorm
    我们发现这个sigmoid函数在小于-4或者大于4的时候他的导数趋近于0。然后我们送进去的input的值在[-100,100]之间,这样很容易引起梯度弥散的现象。所以我们一般情况下使用ReLU函数,但是我们有时候又不得不使用sigmoid函数。这个时候我们在送到下一层的时候我们应该先经过Normalizatio......
  • 服务器日志事件ID4107
    在查看系统日志时,你是否有遇到过事件ID4107错误,来源CAPI2,详细信息在<http://www.download.windowsupdate.com/msdownload/update/v3/static/trustedr/en/authrootstl.cab>从自动更新cab中提取第三方的根目录列表失败,错误为:已处理证书链,但是在不受信任提供程序信任的根证书中终......
  • sloj#P2104. 猎人杀
    题目大意:\(n\)个猎人编号为\(1,2,\cdots,n\)依次按逆时针方向排成一个环。第一枪由你打响,你会向第\((k-1)\bmodn+1(k>0)\)号猎人开枪,这个被击中的猎人有\(\frac12\)​的概率会死亡。所有被击中的猎人(无论死活),都会继续向他的逆时针方向开始的第kkk个(从他......
  • macOS Catalina 10.15.7安装graphviz库
    参考:https://zhuanlan.zhihu.com/p/526601310说明:我通过参考链接里的【方法2:通过软件包管理工具MacPorts,进行间接安装graphviz库】,安装成功pipinstallgraphviz,这样安装的graphviz只是graphviz的调用接口,而并非graphviz程序;graphviz程序,通过sudoportinstallgraphviz在本......
  • deepspeed ZeRO-Inference 可在1-GPU上推理~100B的大模型
    原理:......
  • DPST1091 23T2
    DPST109123T2Assignment1-CSDefenceTowerDefenceisaspecificgenreofgamesthatboomedinthelate2000s.Manyinnovativegameswerecreatedunderthisgenrethatpushednewandexcitingideas!Thecoremechanicsofthesetypesofgamesareactually......