React 合成事件
如果DOM
上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。React
为了避免这类DOM
事件滥用,同时屏蔽底层不同浏览器之间的事件系统差异,实现了一个中间层——SyntheticEvent
。
当用户在为onClick
添加函数时,React
并没有将Click
事件绑定在对应的元素上面,而是在document
处监听所有支持的事件,当事件发生并冒泡至document
处时,React
将事件内容封装交给中间层SyntheticEvent
(负责所有事件合成)
所有的react合成事件都存放在一个事件池里面,当调用的时候给合成事件的事件对象赋值,执行完之后把事件对象设置为null,放回事件池,而不用每次都创建一个新的合成事件
当事件触发的时候,对使用统一的分发函数ReactEventListener.dispatchEvent
将指定函数执行。
合成事件和原生事件的执行顺序是什么?可以混用吗?
React
的所有事件都通过document
进行统一分发。当真实Dom
触发事件后冒泡到document
后才会对React
事件进行处理。- 原生的事件会先执行,然后执行
React
合成事件,最后执行真正在document
上挂载的事件 React
事件和原生事件最好不要混用。原生事件中如果执行了stopPropagation
方法,则会导致其他React
事件失效。因为所有元素的事件将无法冒泡到document
上,导致所有的React
事件都将无法被触发。 在React
中event
是一个SyntheticEvent
,如果和它的交互被延迟了(例如:通过setTimeout
),事件会被清除并且e.target
引用不会再有效,事件对象上的所有属性都为null
import React from 'react'
class SyntheticEvent extends React.Component {
constructor(...props) {
super(...props)
}
clickHandler = (e) => {
setTimeout(() => {
console.log('React 事件触发了', e);
}, 2000)
}
render () {
return (
<>
<button onClick={this.clickHandler}>点击</button>
</>
)
}
}
export default SyntheticEvent
如果你需要在事件处理函数运行之后获取事件对象的属性,你需要调用 e.persist()
:
// ...
clickHandler = (e) => {
e.persist()
setTimeout(() => {
console.log('React 事件触发了', e);
}, 2000)
}
如果原生事件和React事件混用,则优先调用原生事件,再调用React事件,如果document上也绑定了原生事件,则最后执行;
import React from 'react'
class SyntheticEvent extends React.Component {
constructor(...props) {
super(...props)
this.btnRef = React.createRef()
}
clickHandler = (e) => {
console.log('React 事件触发了', e);
}
componentDidMount() {
this.btnRef.current.addEventListener('click', (e) => {
console.log('原生事件', e)
})
document.addEventListener('click', (e) => {
console.log('原生事件 document', e)
})
}
render () {
return (
<>
<button ref={this.btnRef} onClick={this.clickHandler}>点击</button>
</>
)
}
}
export default SyntheticEvent
如果原生事件中阻止冒泡了,则React事件不会触发
// ...
componentDidMount() {
this.btnRef.current.addEventListener('click', (e) => {
console.log('原生事件', e)
e.stopPropagation()
})
document.addEventListener('click', (e) => {
console.log('原生事件 document', e)
})
}
React v17
中,React
不会再将事件处理添加到 document
上,而是将事件处理添加到渲染 React
树的根 DOM
容器中:移除了 “event pooling
(事件池)”
const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);
在 React 16
及之前版本中,React
会对大多数事件进行 document.addEventListener()
操作。React v17 开始会通过调用 rootNode.addEventListener()
来代替。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com