react的合成事件
在没有合成事件前,前端是如何处理事件的呢
假设一个列表的ul标签有一万个li标签,需要添加点击事件,通过点击li获取标签中的文本 应该如何操作:
1.在每个li标签上添加onClick事件 有一万个就需要添加一万个事件,这个是很不友好的,而且影响性能,如何优化呢
通过事件委托,通过将事件绑定到ul上这样的方式,当li事件被点击时,由事件冒泡到父级ul上,并在父级的onClick事件中确认是哪一个li标签
的点事件
<ul id='ii'>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>...</li>
</ul>
let ul = document.getElementById('ii');
ul.onClick = function(event){
e=e || window.event;
return e.target || e.srcElement;
alert(target.innerHTML);
}
react的合成事件:
React 合成事件(SyntheticEvent)是 React 模拟原生 DOM 事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器。它根据 W3C 规范 来定义合成事件,兼容所有浏览器,拥有与浏览器原生事件相同的接口。
在 React 中,所有事件都是合成的,不是原生 DOM 事件,但可以通过 e.nativeEvent 属性获取 DOM 事件。 比如:
const button = <button onClick={handleClick}>react 按钮</button>
const handleClick = (e) => console.log(e.nativeEvent); //原生事件对象
那么 React 为什么使用合成事件?
1.进行浏览器兼容,实现更好的跨平台
React 采用的是顶层事件代理机制,能够保证冒泡一致性,可以跨浏览器执行。React 提供的合成事件用来抹平不同浏览器事件对象之间的差异,将不同平台事件模拟合成事件。
2.避免垃圾回收
事件对象可能会被频繁创建和回收,因此 React 引入事件池,在事件池中获取或释放事件对象。即 React 事件对象不会被释放掉,而是存放进一个数组中,当事件触发,就从这个数组中弹出,避免频繁地去创建和销毁(垃圾回收)。
3.方便事件统一管理和事务机制
React合成事件原理
React合成事件的工作原理大致可以分为两个阶段:
事件绑定
事件触发
在React17之前,React是把事件委托在document上的,React17及以后版本不再把事件委托在document上,而是委托在挂载的容器上了,本文以16.x版本的React为例来探寻React的合成事件。当真实的dom触发事件时,此时构造React合成事件对象,按照冒泡或者捕获的路径去收集真正的事件处理函数,在此过程中会先处理原生事件,然后当冒泡到document对象后,再处理React事件。
import React from 'react';
class Demo extends React.Component {
parentRef: React.RefObject<any>;
childRef: React.RefObject<any>;
constructor(props) {
super(props);
this.parentRef = React.createRef();
this.childRef = React.createRef();
}
componentDidMount() {
document.addEventListener(
'click',
() => {
console.log(`document原生事件捕获`);
},
true,
);
document.addEventListener('click', () => {
console.log(`document原生事件冒泡`);
});
this.parentRef.current.addEventListener(
'click',
() => {
console.log(`父元素原生事件捕获`);
},
true,
);
this.parentRef.current.addEventListener('click', () => {
console.log(`父元素原生事件冒泡`);
});
this.childRef.current.addEventListener(
'click',
() => {
console.log(`子元素原生事件捕获`);
},
true,
);
this.childRef.current.addEventListener('click', () => {
console.log(`子元素原生事件冒泡`);
});
}
handleParentBubble = () => {
console.log(`父元素React事件冒泡`);
};
handleChildBubble = () => {
console.log(`子元素React事件冒泡`);
};
handleParentCapture = () => {
console.log(`父元素React事件捕获`);
};
handleChileCapture = () => {
console.log(`子元素React事件捕获`);
};
render() {
return (
<div
ref={this.parentRef}
onClick={this.handleParentBubble}
onClickCapture={this.handleParentCapture}
>
<div
ref={this.childRef}
onClick={this.handleChildBubble}
onClickCapture={this.handleChileCapture}
>
事件处理
</div>
</div>
);
}
}
export default demo;
注意在react17中
自React发布以来, 一直自动进行事件委托。当 document 上触发 DOM 事件时,React 会找出调用的组件,然后 React 事件会在组件中向上 “冒泡”。但实际上,原生事件已经冒泡出了 document 级别,React 在其中安装了事件处理器。但是,这就是逐步升级的困难所在。
在 React 17 中,React 将不再向 document 附加事件处理器。而会将事件处理器附加到渲染 React 树的根 DOM 容器中
在 React 16 或更早版本中,React 会对大多数事件执行 document.addEventListener()。React 17 将会在底层调用 rootNode.addEventListener()