导言
我最近在学校React事件绑定的时候,发现,React绑定有其自身的一套机制,就是合成事件。
在React中大概是这样绑定事件的
<div className="dome" onClick={this.handleClick}>
而普通的事件绑定是这样的:
<div class="dome" onclick="handleClick()">
React合成事件和原生事件的区别
React合成事件机制: React并不是将click事件直接绑定在dom上面,而是采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数处理运行和处理。
React合成事件详细理解
如果DOM上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。
React为了避免这类DOM事件滥用,同时屏蔽底层不同浏览器之间的事件系统的差异,实现了一个中间层 - SyntheticEvent
-
当用户在为onClick添加函数时,React并没有将Click绑定到DOM上面
-
而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装交给中间层 SyntheticEvent (负责所有事件合成)
-
所以当事件触发的时候, 对使用统一的分发函数 dispatchEvent 将指定函数执行
举个例子
export default class Test extends Component {
constructor() {
super(arguments);
this.onReactClick.bind(this);
}
componentDidMount() {
const parentDom = ReactDOM.findDOMNode(this);
const childrenDom = parentDom.querySelector(".button");
childrenDom.addEventListener('click', this.onDomClick, false);
}
onDomClick() { // 事件委托
console.log('Javascript Dom click');
}
onReactClick() { // react合成事件
console.log('React click');
}
render() {
return (
<div>
<button className="button" onClick={this.onReactClick}>点击</button>
</div>
)
}
}
会打印这个
Javascript Dom click
react-event.jsx:18
如何在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,合成事件监听器被执行。
addEventListener
note:
有点忘记addEventListener第三个参数是什么意思了。现在复习一下
事件冒泡或事件捕获
HTML DOM中有两种事件传播方式,即冒泡和捕获。 事件传播是一种在事件发生时定义元素顺序的方法。
- 冒泡 : 即从里到外
- 捕获 : 即从外到里
使用 addEventListener()方法,可以使用 useCapture 参数来指定传播类型, 默认值为false :
- false : 使用冒泡传播
- true : 使用捕获传播
阻止冒泡
那,如果在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的标记位来确定后续监听器是否执行。
总结
- 合成事件的监听器是统一注册在document上的,并且仅有冒泡阶段。所以原生事件的监听器响应总是比合成事件的监听器早
- 阻止原生事件的冒泡后,会阻止合成事件的监听器执行