react事件机制
1. react的事件是合成事件((Synethic event),不是原生事件
<button onClick={this.handleClick}></button> <input value={this.state.name} onChange={this.handleChange} />
合成事件与原生事件的区别
1. 写法不同,合适事件是驼峰写法,而原生事件是全部小写
2. 执行时机不同,合适事件全部委托到document上,而原生事件绑定到DOM元素本身
3. 合成事件中可以是任何类型,比如this.handleClick这个函数,而原生事件中只能是字符串
2. react合成事件执行过程
3. 事件绑定this写法
React的ES5写法,事件绑定可以自动绑定到组件实例上,而ES6写法却不能,必须使用一些特定的写法。
1. 构造函数bind this
class EventApp extends Component {
constructor(props){
super(props) ;
this._clickMe = this._clickMe.bind(this) ;
}
render(){
return
(<div>
<button onClick={this._clickMe}>点击我</button>
</div>
) ;
}
_clickMe(){
alert("构造方法绑定事件实现方法") ;
}
}
只在构造组件时绑定一次,最佳的方式
2. 元素上bind this
class EventApp extends Component {
render(){
return
(<div>
<button onClick={this._clickMe.bind(this)}>点击我</button>
</div>
) ;
}
_clickMe(){
alert("组件绑定事件实现方法") ;
}
}
每次click会重新生成一个绑定函数,效率不佳
3. 使用箭头函数
class EventApp extends Component {
constructor(props){
super(props) ;
}
render(){
return
(<div>
<button onClick={(e) => this._clickMe(e,"使用箭头函数绑定")}>使用箭头函数绑定事件</button> <p/>
</div>
) ;
}
_clickMe(e,args){
alert("箭头函数绑定事件实现方法") ;
alert(args);
alert(e);
}
}
这种箭头函数定义在render中,组件每渲染一次都会创建一次新的箭头函数,对性能有影响
4. React组件中使用原生事件
由于原生事件绑定在真实DOM上,所以一般是在componentDidMount中或ref回调函数中进行
绑定操作,在componentWillUnmount阶段进行解绑操作,以避免内存泄漏。
class ReactEvent extends Component {
componentDidMount() {
//获取当前真实DOM元素
const thisDOM = ReactDOM.findDOMNode(this);
//或者
const thisDOM = this.refs.con;
thisDOM.addEventListener('click',this.onDOMClick,false);
}
componentWillUnmount() {
//卸载时解绑事件,防止内存泄漏
const thisDOM = ReactDOM.findDOMNode(this);
thisDOM.removeEventListener('click',this.removeDOMClick);
}
onDOMClick(e){
console.log(e)
}
render(){
return(
<div ref="con">
单击原始事件触发
</div>
)
}
}
export default ReactEvent
在componentDidMount周期中,使用addEventListener直接绑定即可,dom元素使用ref或者
ReactDOM.findDOMNode来获取。
合成事件和原生事件可以混合使用,但是尽量避免这种情况出现,因为容易导致混乱,则某些情况下
可以使用。合成事件和DOM事件混合使用,触发顺序是:
1. 先执行原生事件,事件冒泡至document,再执行合成事件
2. 如果是父子元素,触发顺序为 子元素原生事件 -> 父元素原生事件 -> 子元素合成事件 -> 父元素合成事件
如下例子:
class ReactEvent extends Component {
constructor(props){
super(props)
this.state = {
value: ''
}
this.onReactClick = this.onReactClick.bind(this)
this.onReactChildClick = this.onReactChildClick.bind(this)
}
componentDidMount() {
//获取当前真实DOM元素
const thisDOM = ReactDOM.findDOMNode(this);
thisDOM.addEventListener('click',this.onDOMClick,false);
//获取子元素并绑定事件
const thisDOMChild = thisDOM.querySelector(".child");
thisDOMChild.addEventListener('click',this.onDOMChildClick,false);
}
onDOMClick(e){
console.log("父组件原生事件绑定事件触发")
} onReactClick(e){
console.log("父组件React合成事件绑定事件触发")
}
onDOMChildClick(e){
e.stopPropagation()
console.log("子元素原生事件绑定事件触发")
}
onReactChildClick(e){
console.log("子元素React合成事件绑定事件触发")
}
render(){
return(
<div onClick={this.onReactClick}>
父元素单击事件触发
<button className="child" onClick={this.onReactChildClick}>子元素单击事件触发</button>
</div>
)
}
}
export default ReactEvent
通过设置原生事件绑定为冒泡阶段调用,且每次测试单击子元素按钮:
1.在子元素原生事件程序中阻止事件传播,则打印出:
子元素原生事件绑定事件触发;
2.在父元素元素事件程序中阻止事件传播,则打印出:
子元素原生事件绑定事件触发
父组件原生事件绑定事件触发
3.在子元素React合成事件onClick中阻止事件传播,则打印出:
子元素原生事件绑定事件触发
父组件原生事件绑定事件触发
子元素React合成事件绑定事件触发
4.在父元素React合成事件onClick中阻止事件传播,则打印出:
子元素原生事件绑定事件触发
父组件原生事件绑定事件触发
子元素React合成事件绑定事件触发
父组件React合成事件绑定事件触发
可以看到若不阻止事件传播每次(单击子元素)事件触发流程是:
Document->子元素(原生事件触发)->父元素(原生事件)->回到Document->React子元素合成事件监听器触发 ->React父元素合成事件监听器触发
5. 合成事件与阻止冒泡
基本原则
阻止合成事件的冒泡不会阻止原生事件的冒泡,但是阻止原生事件的冒泡会阻止合成事件的冒泡。
1. 阻止合成事件冒泡,用e.stopPropagation()
例如:
render() {
return
<div onClick={this.outClick}>
<button onClick={this.onClick}> 测试click事件 </button>
</div>
}
outClick是外层合成事件,用e.stopPropagation会阻止其运行,但是不能阻止原生事件,例如document上用
addEventListener绑定的事件
2. 阻止原生事件和合成事件冒泡(因为阻止了原生事件就会阻止合成事件),用e.nativeEvent.stopImmediatePropagation();
3. 在document或body上注册的原生事件方法,可以通过e.target判断来阻止冒泡事件的执行
例如存在如下的业务场景: 点击input框展示日历,点击文档其他部分,日历消失,代码如下:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showCalender: false
};
}
componentDidMount() {
document.addEventListener('click', (e) => {
var tar = document.getElementById('myInput');
//判断e.target使得document事件不往下执行
if (tar.contains(e.target)) return;
console.log('document!!!');
this.setState({showCalender: false});
}, false);
}
render() {
return (<div>
<input
id="myInput"
type="text"
onClick={(e) => {
this.setState({showCalender: true});
console.log('it is button')
// e.stopPropagation();
}}
/>
<Calendar isShow={this.state.showCalender}></Calendar>
</div>);
}
}
其他的处理方法,也就是input也使用原生事件来阻止冒泡,或者使用stopImmediatePropagation
7. 合成事件执行过程
React不会将事件处理函数直接绑定到真实的节点上,而是把所有的事件绑定到结构的最外层,使用一个统一的事件监听器。
这个监听器维持了一个映射,保存所有组件内部的事件监听和处理函数。当事件发生时,首先被这个统一的事件监听器处理,
然后在映射里找到真正的事件处理函数并调用。
合成事件分为以下三个主要过程:
一 事件注册
所有事件都会注册到document上,拥有统一的回调函数dispatchEvent来执行事件分发
二 事件合成
从原生的nativeEvent对象生成合成事件对象,同一种事件类型只能生成一个合成事件Event,如onclick这个类型的事件,dom上所有带有通过jsx绑定的onClick的回调函数都会按顺序(冒泡或者捕获)会放到Event._dispatchListeners 这个数组里,后面依次执行它
三 事件派发
每次触发事件都会执行根节点上 addEventListener 注册的回调,也就是 ReactEventListener.dispatchEvent 方法,事件分发入口函数。该函数的主要业务逻辑如下:
1. 找到事件触发的 DOM 和 React Component
2. 从该 React Component,调用 findParent 方法,遍历得到所有父组件,存在数组中。
3. 从该组件直到最后一个父组件,根据之前事件存储,用 React 事件名 + 组件 key,找到对应绑定回调方法,执行,详细过程为:
4. 根据 DOM 事件构造 React 合成事件。
5. 将合成事件放入队列。
6. 批处理队列中的事件(包含之前未处理完的,先入先处理)
React合成事件的冒泡并不是真的冒泡,而是节点的遍历。
8. React事件处理的特性
React的事件系统和浏览器事件系统相比,主要增加了两个特性:事件代理和事件自动绑定
1、事件代理
1. 区别于浏览器事件处理方式,React并未将事件处理函数与对应的DOM节点直接关联,而是在顶层使用了一个全局事件监听器监听所有的事件;
2. React会在内部维护一个映射表记录事件与组件事件处理函数的对应关系;
3. 当某个事件触发时,React根据这个内部映射表将事件分派给指定的事件处理函数;
4. 当映射表中没有事件处理函数时,React不做任何操作;
5. 当一个组件安装或者卸载时,相应的事件处理函数会自动被添加到事件监听器的内部映射表中或从表中删除。
2、事件自动绑定
1. 在JavaScript中创建回调函数时,一般要将方法绑定到特定的实例,以保证this的正确性;
2. 在React中,每个事件处理回调函数都会自动绑定到组件实例(使用ES6语法创建的例外);
注意:事件的回调函数被绑定在React组件上,而不是原始的元素上,即事件回调函数中的
this所指的是组件实例而不是DOM元素;
3、合成事件
1. 与浏览器事件处理稍微有不同的是,React中的事件处理程序所接收的事件参数是被称为“合成事件(SyntheticEvent)”的实例。
合成事件是对浏览器原生事件跨浏览器的封装,并与浏览器原生事件有着同样的接口,如stopPropagation(),preventDefault()等,并且
这些接口是跨浏览器兼容的。
2. 如果需要使用浏览器原生事件,可以通过合成事件的nativeEvent属性获取
9. React在事件处理的优点
1. 几乎所有的事件代理(delegate)到 document ,达到性能优化的目的
2. 对于每种类型的事件,拥有统一的分发函数 dispatchEvent
3. 事件对象(event)是合成对象(SyntheticEvent),不是原生的
4. react内部事件系统实现可以分为两个阶段: 事件注册、事件分发,几乎所有的事件均委托到document上,而document上事件的回调函数只有一个: ReactEventListener.dispatchEvent,然后进行相关的分发
5. 对于冒泡事件,是在 document 对象的冒泡阶段触发。对于非冒泡事件,例如focus,blur,是在 document 对象的捕获阶段触发,最后在 dispatchEvent 中决定真正回调函数的执行
参考: https://www.jianshu.com/p/99dc37f9edf3
https://blog.csdn.net/qq_38160012/article/details/80679420
https://zhuanlan.zhihu.com/p/26742034
http://www.open-open.com/lib/view/open1488868982317.html
https://segmentfault.com/a/1190000013364457
react事件机制的更多相关文章
- 【React】354- 一文吃透 React 事件机制原理
大纲 主要分为4大块儿,主要是结合源码对 react事件机制的原理 进行分析,希望可以让你对 react事件机制有更清晰的认识和理解. 当然肯定会存在一些表述不清或者理解不够标准的地方,还请各位大神. ...
- 源码看React 事件机制
对React熟悉的同学都知道,React中的事件机制并不是原生的那一套,事件没有绑定在原生DOM上,发出的事件也是对原生事件的包装.那么这一切是怎么实现的呢? 事件注册 首先还是看我们熟悉的代码 &l ...
- 总结react native 事件机制
React 事件机制 一个组件的所有事件会使用统一的事件监听器,绑定到组件的最外层,那么如何使用? bind方法,绑定并且可以传递参数 <TouchableOpacity onPress={th ...
- React 为什么要把事件挂载到 document 上 & 事件机制源码分析
前言 我们都知道 React 组件绑定事件的本质是代理到 document 上,然而面试被问到,为什么要这么设计,有什么好处吗? 我知道肯定不会是因为虚拟 DOM 的原因,因为 Vue 的事件就能挂载 ...
- 深入理解React:事件机制原理
目录 序言 DOM事件流 事件捕获阶段.处于目标阶段.事件冒泡阶段 addEventListener 方法 React 事件概述 事件注册 document 上注册 回调函数存储 事件分发 小结 参考 ...
- React事件杂记及源码分析
前提 最近通过阅读React官方文档的事件模块,发现了其主要提到了以下三个点 调用方法时需要手动绑定this React事件是一种合成事件SyntheticEvent,什么是合成事件? 事件属性 ...
- javaScript tips —— z-index 对事件机制的影响
demo // DOM结构 class App extends React.Component { componentDidMount() { const div1 = document.getEle ...
- 【移动端兼容问题研究】javascript事件机制详解(涉及移动兼容)
前言 这篇博客有点长,如果你是高手请您读一读,能对其中的一些误点提出来,以免我误人子弟,并且帮助我提高 如果你是javascript菜鸟,建议您好好读一读,真的理解下来会有不一样的收获 在下才疏学浅, ...
- tkinter事件机制
一.tkinter.Event tkinter的事件机制跟js是一样的,也是只有一个Event类,这个类包罗万象,集成了键盘事件,鼠标事件,包含各种参数. 不像java swing那种强类型事件,sw ...
随机推荐
- Git笔记——01
Git - 幕布 Git 教程:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b00 ...
- 利用JSON Schema校验JSON数据格式
最近笔者在工作中需要监控一批http接口,并对返回的JSON数据进行校验.正好之前在某前端大神的分享中得知这个神器的存在,调研一番之后应用在该项目中,并取得了不错的效果,特地在此分享给各位读者. 什么 ...
- DirectX11与DirectX12在古墓丽影暗影中的表现
最近在关注这两个图形API,因为感兴趣,也算是初学者. 以下内容仅供参考. 使用古墓丽影暗影游戏,分别对这两个进行比较,得出的结论如下图(此笔记本散热很差,更改散热应该比下图结果好些): 首先看可以很 ...
- Linux命令应用大词典-第43章iptables和arptables防火墙
43.1 iptables-save:保存iptables规则 43.2 iptables-restore:恢复iptables规则 43.3 iptables:IPv4数据包过滤和NAT管理工具 4 ...
- kettle_简单入门
简介 Kettle是一款纯Java开发的ETL工具,它是跨平台的,所以它可以在Window.Linux.Unix上运行.注意什么是ETL,读者可以自行百度了解,我的理解是将一个数据库的数据导入到另外一 ...
- Java 集合学习--HashMap
一.HashMap 定义 HashMap 是一个基于散列表(哈希表)实现的键值对集合,每个元素都是key-value对,jdk1.8后,底层数据结构涉及到了数组.链表以及红黑树.目的进一步的优化Has ...
- js for循环实例
1.求1-100的寄数和? //2.奇数求和 var ppt=0 for(var i=1;i<=100;i+=2){ ppt+=i } 2.求1-100的偶数和 var num=0 for(va ...
- leetcode-数数并说
数数并说 报数序列是指一个整数序列,按照其中的整数的顺序进行报数,得到下一个数.其前五项如下: 1. 1 2. 11 3. 21 4. 1211 5. 111221 1 被读作 " ...
- python学习摘要(2)--基本数据类型
python申请存储空间是动态的.变量如同指针一样指向存储空间.多个变量会指向同一个存储空间(节省空间).当变量改变时,原来的地址单元并不会马上释放.(引用计数自行回收) c/c++根基性语言,想要什 ...
- Ubuntu编译内核树
什么是内核树?刚开始我也没弄明白,通过这几天的学习,有所感悟,就说说我的理解吧!从形式上看,内核树与内核源码的目录结构形式是相同的,都是由各个层次的文件目录结构组成,但是其中的具体内容肯定是不同的.从 ...