在前文中,我们初步了解了react 的事件的使用方式,并且只是了解到react 的语法和原生的语法很相似。但是真正的事件原理,其实是大不相同的。
React中的事件
这里的事件:React内置的DOM组件中的事件
1. 注册事件
在react 中,给组件注册的事件,不是真正的给组件内的div,或者是其他的元素注册的事件,而是通过事件冒泡的形式,最终都是给document注册的事件。(react 16版本),在react17版本上,事件就不是冒泡到document上了,而是在虚拟的根节点中进行注册.
2. 事件冒泡
1.一些不冒泡的事件,是直接在元素上监听,如input 框中的focus事件
2.一些document上面没有的事件,直接在元素上监听
样例
click 事件是会冒泡的
import React, { PureComponent } from 'react' export default class TestCompEventPro extends PureComponent { handleBtnClick = () => { console.log('我是一个按钮被点击了'); } render() { return ( <div> <button onClick={this.handleBtnClick}>我是一个按钮</button> </div> ) } } // 所有的react 组件都是挂载在root这个真实的dom下面。 document.getElementById('root')!.addEventListener('click', ()=> { console.log('我注册了一个点击事件'); })
效果
3. React会根据虚拟DOM树的完成事件函数的调用
16版本是在document进行事件处理, 17版本的是在react 的虚拟跟节点完成事件函数的调用。
4. React的事件参数,并非真实的DOM事件参数,是React合成的一个对象,该对象类似于真实DOM的事件参数
1.stopPropagation,阻止事件在虚拟DOM树中冒泡
案列
import React, { MouseEvent, PureComponent } from 'react' export default class TestCompEventPro extends PureComponent { handleBtnClick = () => { } render() { return ( <div> <button onClick={(e:MouseEvent<HTMLButtonElement>) => { console.log('我是一个按钮被点击了'); e.nativeEvent.stopPropagation(); e.stopPropagation(); }}>我是一个按钮</button> </div> ) } } // 根节点 <div onClick={(e: React.MouseEvent) => { console.log('阻止事件'); }}>
结果:
2.nativeEvent,可以得到真实的DOM事件对象
案列
render() { return ( <div> <button onClick={(e:MouseEvent<HTMLButtonElement>) => { console.log('我是一个按钮被点击了'); console.log( e.nativeEvent) }}>我是一个按钮</button> </div> ) }
结果:
4.为了提高执行效率,16版本React使用事件对象池来处理事件对象,在17版本中,这个事件池被移除了
注意事项
1.16版本,如果给真实的DOM注册事件,stopPropagation阻止了事件冒泡,则会导致react的相应事件无法触发,17版本中 需要使用 stopImmediatePropagation
2.如果给真实的DOM注册事件,事件会先于React事件运行,
3.通过React的事件中阻止事件冒泡,无法阻止真实的DOM事件冒泡
4.可以通过nativeEvent.stopImmediatePropagation(),阻止document上剩余事件的执行
5.在事件处理程序中,不要异步的使用事件对象,如果一定要使用,需要调用persist函数(16版本),17版本中这个函数没有实际意义了