最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React事件委托机制详解

    正文概述 掘金(Bpuns)   2021-02-12   1533

    1 React16事件委托

    1.1 document

    React17之前的版本上,在虚拟dom上绑定的事件,都会被放入到一个事件池中

    然后React全局只要给document绑定一此事件,在这个事件中监听事件冒泡事件

    React再去事件池中找到对应的dom元素与对应的事件将其触发

    React事件委托机制详解

    1.2 验证

    这里有一个例子,在React注册之前document上绑定一个事件(保证自己的事件比React的document事件先触发),然后在真实的dom上绑定一个事件和一个React合成事件。

    等待页面渲染完成之后,点击按钮验证

    • 准备一个React组件
    export default function () {
    
      const buttonRef = useRef() as React.MutableRefObject<HTMLButtonElement>;
    
      useEffect(() => {
        buttonRef.current.addEventListener('click', function (e) {
          console.log('真实的button的dom事件')
        })
      }, [])
    
      const handleClick = useCallback(() => {
        console.log('绑定在虚拟dom上的button事件')
      }, [])
    
      return <button ref={buttonRef} onClick={handleClick}>按钮</button>
    
    }
    

    然后在React依赖插入之前在document上绑定一个事件

    React事件委托机制详解

    之后点击按钮,发现控制台打印如下

    真实的button的dom事件
    document上的事件
    绑定在虚拟dom上的button事件
    

    这就能验证React把所有事件全部挂载到document上这个机制了


    甚至,可以在真实事件中阻止事件冒泡,然后就会发现,document事件和React的事件全部都没有触发

    React事件委托机制详解

    点击按钮后,控制台打印如下

    真实的button的dom事件
    

    1.3 如何实现

    当事件冒泡到document上之后,在事件触发的回调Event中,有一个path参数

    里面详细的描述了冒泡顺序

    React事件委托机制详解

    1.4 函数触发参数

    可以看下React中事件绑定回调 和 真实dom绑定回调的对象区别

    button.addEventListener('click', function (e) {
      console.log(e)
    })
    
    <button 
      id="button"
      onClick={(e)=>{
        console.log(e)
      }}
    >
      按钮
    </button>
    

    可以看到,两者的回调是不同的

    React事件委托机制详解

    TypeScript 中,这两者的类型也是不同的

    // 真实dom
    button.addEventListener('click', function (e: MouseEvent) {})
    
    // React合成事件
    function(e:React.MouseEvent<HTMLButtonElement>){}
    

    如果想在合成事件参数中获取原生的MouseEvent,可以访问这个参数

    e.nativeEvent
    

    1.5 persist

    React为了把事件绑定性能做到了极致,做了一件事,当绑定的事件结束之后

    合成事件回调对象 就会被立马销毁。下面的例子可以证明此事

    const handleClick = useCallback((e) => {
      console.log(e)
      setTimeout(() => console.log(e))
    }, [])
    

    React事件委托机制详解

    可以在事件执行中调用 e.presist 来保存状态

    const handleClick = useCallback((e) => {
      e.presist();
      console.log(e)
      setTimeout(() => console.log(e))
    }, [])
    

    React事件委托机制详解

    2 React中的this

    2.1 this消失

    说完合成事件之后,再看一下为什么在 class 组件中为什么事件都要进行绑定 this

    首先,在js中,this指向其调用者,如下

    const obj = {
      name: 'obj',
      func() {
        console.log(this)
      }
    }
    
    obj.func()  // {name: "obj", func: ƒ}
    

    如果把func提取出来执行,那么func在模块化或严格模式中打印的就是undefined

    const func = obj.func;
    
    func();   // undefined
    

    那这个操作就很熟悉了,组件代码如下

    class App extends Component {
    
      handleClick() {
        console.log('handleClick')
      }
      
      render() {
        return (
          <button onClick={this.handleClick}>按钮</button>
        );
      }
    
    }
    

    this.handleClick 中的 handleClick 被提取出来,放在一个事件池中 [......]

    之后被触发,这个时候,this 早已经不是指向那个组件了

    2.2 绑定this指向

    在以前,this绑定一共又三种方案,并且三种方案都有利弊

    • 虚拟dom直接bind绑定
    <button onClick={this.handleClick.bind(this)}>按钮</button>
    

    缺陷: 每当组件更新,函数就会bind一次,影响性能

    • 构造函数绑定
    constructor(){
    	this.handleClick = this.handleClick.bind(this)
    }
    

    缺陷: 每个函数都要bind一次,导致代码冗余臃肿

    • 直接绑定一共匿名函数
    <button onClick={()=>this.handleClick()}>按钮</button>
    

    缺陷: 上面两个缺陷集合体

    2.3 箭头函数

    箭头函数的this指向永远指向其函数所在的位置的this指向,如下

    const obj = {
      name: 'obj',
      func() {
        const exec = () => console.log(this);
        exec();
      }
    }
    
    obj.func();   // {name: "obj", func: ƒ}}
    

    所以,在组件中可以这么写

    class App extends Component {
    
      handleClick = () => {
        console.log('handleClick')
      }
    
      render() {
        return (
          <button onClick={this.handleClick}>按钮</button>
        );
      }
    
    }
    

    3 React 17事件委托

    先将项目中的React版本升级到17版本

    $ yarn add react@17.0.1 react-dom@17.0.1
    

    还是举刚才 React 把 事件绑定在 document 上的例子验证,一摸一样,只是把 React 版本升级到17

    React16的执行顺序如下

    真实的button的dom事件
    document上的事件
    绑定在虚拟dom上的button事件
    

    那么React17 的执行顺序变成了这样

    真实的button的dom事件
    绑定在虚拟dom上的button事件
    document上的事件
    

    这是因为 React17 支持在页面中共存多个 React版本 ,如果把所有的事件全部绑定在一共document上,便会出现问题

    所以,在17版本上,事件委托不放在 document 上,而是放在执行的根节点上,如 #root

    ReactDOM.render(
      <HelloWorld />,
      document.getElementById('root') as HTMLElement
    );
    

    React事件委托机制详解


    起源地下载网 » React事件委托机制详解

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元