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 ...
随机推荐
- Selenium 入门到精通系列:三
Selenium 入门到精通系列 PS:Driver_Element 常用方法 例子 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2 ...
- 第三模块:面向对象&网络编程基础 第2章 网络编程
01-计算机基础 02-什么是网络 03-五层协议详解 04-传输层详解 05-什么是Socket 06-基于socket实现简单套接字通信 07-在简单套接字基础上加上通信循环 08-客户端与服务端 ...
- TW实习日记:第19天
今天一早上改完信息门户的代码之后,发现接口又出了问题,查了半天都不知道,原来又是网端的问题...真是心累啊,调整了一些细节样式,以及终于把企业微信的消息推送功能做完了.关键就在于有个表存放微信id的字 ...
- vector:动态数组
vector是C++标准模板库中的部分内容,中文偶尔译作“容器”,但并不准确.它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被认为是一个容器,是因为它能够像容器一样存 ...
- Asp.net之数组应用
string[] abc=new string[8]{"1","2","3","4","1",&qu ...
- axis2调用webService几种方式
主要有三种方式: 第一RPC方式,不生成客户端代码 第二,document方式,不生成客户端代码 第三,用wsdl2java工具,生成客户端方式调用 java代码: package samples.q ...
- “hello world!”团队第三次会议
团队“hello world!”团队召开的第三次会议.博客内容: 一.会议时间 二.会议地点 三.会议成员 四.会议内容 五.todo list 六.会议照片 七.燃尽图 一.会议时间 2017年10 ...
- “Hello World!”团队第二次会议
今天是我们团队“hello world!”团队召开的第二次会议.博客内容: 一.会议时间 二.会议地点 三.会议成员 四.会议内容 五.todo list 六.会议照片 七.燃尽图 一.会议时间 20 ...
- 算法与数据结构5.2 Bubble Sort
★实验任务 给定一个 1~N 的排列 P,即 1 到 N 中的每个数在 P 都只出现一次. 现在要 对排列 P 进行冒泡排序,代码如下: for (int i = 1; i <= N; ++i) ...
- 求gcd(最大公因数),lcm(最小公倍数)模板
gcd(最大公因数),lcm(最小公倍数) #include<iostream> using namespace std; int gcd(int a,int b)//辗转相除法(欧几里德 ...