React合成事件和原生事件

Posted Sco_Jing1031

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React合成事件和原生事件相关的知识,希望对你有一定的参考价值。

欢迎学习交流!!!
持续更新中…

文章目录


事件流

'流’这个名词在JS中随处可见。像DOM事件流、React中的数据流等等。
‘流’是一种有方向的数据;事件流,是页面接受事件的顺序。

DOM事件流的几个阶段

  • 事件捕获阶段

当某个事件触发时,文档根节点最先接受到事件,然后根据DOM树结构向具体绑定事件的元素传递。该阶段为父元素截获事件提供了机会。
事件传递路径为:
window —> document —> body —> button

  • 目标阶段

具体元素已经捕获事件,之后事件开始向根节点冒泡。

  • 事件冒泡阶段

该阶段的开始即是事件的开始,根据DOM树结构由具体触发事件的元素向根节点传递。
事件传递路径:
button —> body —> document —> window

选择监听事件的阶段:

使用addEventListener函数在事件流的的不同阶段监听事件,

DOM.addEventListener(‘事件名称’,handleFn,Boolean);

第三个参数Boolean即代表监听事件的阶段:
为true时,在捕获阶段监听事件;
为false时,在冒泡阶段监听事件

事件冒泡和事件捕获:
html DOM中有两种事件传播方式,冒泡和捕获方式。事件传播是一种在事件发生时定义元素顺序的方法

  • 冒泡:从里到外
  • 捕获:从外到里

React合成事件

React在事件绑定时有一套自身的机制,就是合成事件。

react中事件绑定:
<div className="dome" onClick=this.handleClick>
普通的事件绑定:
<div class="dome" onclick="handleClick()">

React合成事件机制:
React并不是将click事件直接绑定在dom上面,而是采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数处理运行和处理。

React合成事件原理

若react事件绑定在真实的DOM节点上,一个DOM节点绑定过多事件处理函数,整个页面的响应和内存占用可能都会受到很大的影响。
React为了避免这类DOM事件的滥用,同时屏蔽底层不同浏览器之间的时间系统的差异,出现了一个中间层——SyntheticEvent。

  1. 当用户在为onClick添加函数时,React并没有将Click绑定到DOM上
  2. 而在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装交给中间层 SyntheticEvent (负责所有事件合成)
  3. 然后使用统一的分发函数 dispatchEvent 将封装的事件内容交由真正的处理函数执行


注:
由于class的方法默认不会绑定this,若没有绑定this,调用this的时候为undefined

 export default class App extends React.Component 
	show()
     	console.log(this)  
    
    render () 
    	return   <button onClick= this.show >点击</button>
    

解决方法:

  • 在构造函数中使用bind绑定this。在构造函数constructor内绑定this,优点是只需要绑定一次,避免每次渲染时的重新绑定,函数在别处复用时也无需再次绑定
export default class App extends React.Component  
constructor(props) 
    super(props);
    this.show = this.show.bind(this);
  
  show()
     	console.log(this)  
    
  render() 
    return  <button onClick= this.show >点击</button>
    ;
  

  • 在调用的时候使用bind绑定this。直接绑定方法,缺点是每次渲染都会重新绑定一次bind
export default class App extends React.Component  
  show()
    console.log(this);
  
  render() 
    return   <button onClick=this.show.bind(this)>点击</button>
  

  • 在调用时用箭头函数绑定this。箭头函数不仅仅是函数的“语法糖”,还自动绑定了定义此函数作用域的this,因此不需要再对它使用bind方法
export default class App extends React.Component 
  show()
    console.log(this);
  
  render() 
    return <button onClick=()=>this.show()>点击</button>
  

在react中使用原生事件方法

react封装了大多数的原生事件,但也有个例:

  • Modal开启以后点击其他空白区域需要关闭Modal
  • 引入了一些以原生事件实现的第三方库,并且互相之间需要有交互

由于原生事件需要绑定在真实DOM上,所以一般是在componentDidMount阶段/ref的函数执行阶段进行绑定操作,在componentWillUnmount阶段进行解绑操作以避免内存泄漏。

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

若业务场景中需要混合使用合成事件和原生事件,使用过程中需注意:

响应顺序

class Demo extends React.Component 
    componentDidMount() 
        const $this = ReactDOM.findDOMNode(this)
        $this.addEventListener('click', this.onDOMClick, false)
    

    onDOMClick = evt => 
        console.log('dom event')
    
    
    onClick = evt => 
        console.log('react event')
    

    render() 
        return (
            <div onClick=this.onClick>Demo</div>
        )
    

分析:首先DOM事件监听器被执行,然后事件继续冒泡至document,合成事件监听器被执行。

阻止冒泡

如果在onDOMClick中调用evt.stopPropagation()方法,由于DOM事件被阻止冒泡了,无法到达document,所以合成事件自然不会被触发

export default class Test extends Component 
    componentDidMount() 
        const $parent = ReactDOM.findDOMNode(this);
        const $child = $parent.querySelector('.child');
        $parent.addEventListener('click', this.onParentDOMClick, false)
        $child.addEventListener('click', this.onChildDOMClick, false)
    

    onParentDOMClick = evt => 
        console.log('parent dom event')
    

    onChildDOMClick = evt => 
        console.log('child dom event')
    

    onParentClick = evt => 
        console.log('parent react event')
    

    onChildClick = evt => 
        evt.stopPropagation();
        console.log('child react event')
    

    render() 
        return (
            <div onClick=this.onParentClick>
                <div className="child" onClick=this.onChildClick>
                    Demo
                </div>
            </div>
        )
    

onChildClick中调用evt.stopPropagtion(),合成事件不会被触发是因为React给合成事件封装的stopPropagation函数调用时给自己加了isPropagationStopped的标记位来确定后续监听器是否执行。

总结

  • class方法默认不会绑定this,需要在构造函数constructor内使用bind绑定this
  • 合成事件的监听器是统一注册在document上的,并且仅有冒泡阶段。所以原生事件的监听器响应总是比合成事件的监听器早
  • 阻止原生事件的冒泡后,会阻止合成事件的监听器执行

参考文章:

以上是关于React合成事件和原生事件的主要内容,如果未能解决你的问题,请参考以下文章

React合成事件和原生事件

react合成事件——bind解决this指向——箭头函数解决this指向

React 合成事件 SyntheticEvent

react事件注册

React事件

写给进阶玩家的 React 事件系统原理