版权声明:本文为博主原创文章,未经博主允许不得转载。
PS:转载请注明出处
作者:TigerChain
地址:http://www.jianshu.com/p/99dc37f9edf3
本文出自TigerChain简书
教程简介
- 1、阅读对象
本篇教程适合初学者,老鸟直接略过,如果有误,欢迎指出,谢谢。
-
2、教程难度
初级
3、Demo 地址:https://github.com/tigerchain/react-lesson/tree/master/lesson03/12-eventsys
4、本节 demo 效果图如下:
一、事件和事件系统?
1、什么事件?
事件是一个宏观的概念,在各个领域中含义也不太一样。一般来说 事件就是:比较重大,对一定的人群会产生一定影响的事情。
但是在编程中的事件,是一个抽像的概念,可以理解就是干什么(动作),比如:点击一个按钮是一个事件,刷新网页是一个事件,拖拽,移动等等都是是事件。
事件不能单独工作,事件和事件源,监听器三者联合起来才可以工作,所以我们也可以说,事件的三剑客就是,事件、事件源和监听器。
举个栗子!!!
比如点击一个按钮显示出一句话:" Hello React " ! ,那么分析一下这一事件的过程。
事件:点击
事件源:按钮(发生事件的对象也叫事件的产生器)
监听器:监听着事件处理结果,一般是一个回调函数。
2、什么是事件系统?
软件开发中的事件系统也就是事件处理系统,它包含事件监听,事件分发,事件处理回调等等一系列过程。(个人理解,如有不同理解可以指出)。每个开发平台有自己不同的事件处理机制。
二、React 事件系统
1、跨浏览器使用
React 标准化了事件对象,和浏览器本地事件是是同一个接口( React 也就是把浏览器的事件包装了一把而已),所以它可以工作在所有的浏览器中,可以直接在所有的浏览器使用 React 的事件。
2、React 的事件分类
1、合成事件
一、React 事件和 HTML 事件
在 React 中绑定事件和 HTML 中绑定事件类似,但是还是有以下不同。
- 1、React 事件的命名是驼峰标志,比如: onClick 而不能是 onclick。
- 2、在 JSX 中你可以传一个方法去处理函数,而不是一个字符串。
- 3、React事件并没有原生的绑定在真实的DOM上,而是使用了 行为委托 方式实现事件机制。
举个例子
在 HTML 中一个按钮的点击事件
<button onclick="showImg()">显示图片</button>
在 React 中一个按钮的点击事件
<button onClick={showImg}>显示图片</button>
PS:
关于 HTML 事件可以看这里:http://www.runoob.com/jsref/dom-obj-all.html
二、合成事件
用官网的话说:React 实现了一个“合成事件”层(synthetic event system),这个事件模型保证了和 W3C 标准保持一致,所以不用担心有什么诡异的用法,并且这个事件层消除了 IE 与 W3C 标准实现之间的兼容问题。
绑定事件,在 React 合成事件中有三种绑定事件的方式:
- 1、组件上绑定。
格式:
<Component 事件={this.方法.bind(this)}></Component>
实例
import React ,{Component} from 'react';
class EventApp extends Component {
render(){
return
(<div>
<button onClick={this._clickMe.bind(this)}>点击我</button>
</div>
) ;
}
_clickMe(){
alert("组件绑定事件实现方法") ;
}
}
PS:每次点击的时候都要重新绑定一个函数,我们知道函数的创建和销毁是需要开销的,所以这种方式对性能有影响。
- 2、构造方法中绑定。
格式:
constructor(props){
super(props) ;
this.方法 = this.方法.bind(this,'event','args') ;
// event(事件名) 和 args(参数) 不是必须的。
}
<Component 事件={this.方法}></Component>
实例
import React ,{Component} from 'react';
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("构造方法绑定事件实现方法") ;
}
}
使用构造方法绑定事件,只需要绑定一次。而不用每次使用的时候都去绑定。
- 3、使用箭头函数。
格式:
<Component 事件={(e) => this.方法(e,args)}</Component>
//其中 args (参数)不是必须的。
实例
import React ,{Component} from 'react';
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);
}
}
PS:使用箭头函数系统就默认把 this 绑定到了 _clickMe 方法中,但是需要注意一点,由于箭头函数绑定是定义在 redner 方法中的,组件的每一次渲染都会创建一个新的箭头函数 [ React 中渲染是经常会发生的]。这种方式和组件上绑定是一个道理,对性能有影响。
总结: 综上所述,我个人建议使用在构造方法中绑定事件的方法这种方式。
三、合成事件绑定事件的处理函数
React 合成事件绑定事件处理的函数涉及到鼠标类,触摸类,键盘类,表单和焦点等。
具体可以看官方 Supported Events 这一小节: https://facebook.github.io/react/docs/events.html,这没有什么好说的。
注意:经过上面的分析,我们知道 React 的事件绑定有三种方式,但是我们要注意一点,我们不能直接在组件中监听事件。因为组件是 DOM 元素的包装器,如果在组件中直接监听事件,那么到底事件应该是对应那个 DOM 元素的,是一个 button 的还是一张图片的,你就分不清楚了。这样你也分不清楚它和 props 有啥区别。
比如:定义一个含有按钮的子组件 ShowPlus.js 。然后在父组件中监听事件。
1、错误做法
# ShowPlus.js
import React, { Component, PropTypes } from 'react';
export default class ShowPlus extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<button> 点我 </button>
</div>);
}
}
父组件,姑且叫 FatherCom.js
# FatherCom.js
import React, { Component, PropTypes } from 'react';
import ShowPlus from './ShowPlus.js' ;
export default class FatherCom extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<ShowPlus onClick={this._addPlus}/>
</div>);
}
_addPlus(){
alert("this is a plus") ;
}
}
以上方式是错误的,你把按钮点死它也不会调用的,所以我们不要直接在一个自定义组件上添加监听器。
那么我们如何处理这种情况呢,看过 props 这一节的朋友很快就会有答案的,我们可以把事件处理器当作 props 传递到子组件即可。
2、正确的做法
# ShowPlus.js
import React, { Component, PropTypes } from 'react';
export default class ShowPlus extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<button onClick={this.props.clickHandler}> 点我 </button>
</div>);
}
}
# FatherCom.js
import React, { Component, PropTypes } from 'react';
import ShowPlus from './ShowPlus.js' ;
export default class FatherCom extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<ShowPlus clickHandler={this._addPlus}/>
</div>);
}
_addPlus(){
alert("this is a plus") ;
}
}
这样我们就采用一个变通的方法,把事件处理器当作一个 props 传递给子组件,然后在组件内部,我们把事件监听到一个 DOM 元素上,并将事件处理设置为我们传递过来的 props 就解决了这种问题。
2、原生事件
原生事件就是浏览器所对应的事件,一般情况下,我们使用 React 提供的合成事件就能满足大部分需求,但是有些情况下,我们不得不使用原生事件,所以必要时合成事件和原生事件要搭配着使用。
什么是原生事件?
比如,你在 componentDidMount 方法中通过 addEventListener 绑定的事件就是原生事件。
由于不是所有的 DOM 都有合成事件的对应物。我们不妨试一下"自定义"一个事件。
1、自定义事件
错误的例子:
import React, { Component, PropTypes } from 'react';
/**
* 自定义事件
*/
export default class CustomEvent extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<button onCusTomAlert={this._customAlert.bind(this)}>自定义事件</button>
</div>);
}
_customAlert(e){
alert("custom event") ;
}
}
在上面我们自定义了一个 onCusTomAlert 事件,这是 React 中是行不通的,对于 React 不能识别的事件,我们就要使用原生传统的事件方式 addEventListener 处理。修改上面的代码。
正确的例子:
import React, { Component, PropTypes } from 'react';
/**
* 自定义事件
*/
export default class CustomEvent extends Component {
constructor(props) {
super(props);
}
// 组件挂载之后注册监听器
componentDidMount(){
window.addEventListener("click", this._customAlert);
}
//组件卸载之后移除监听器
componentWillUnmount(){
window.removeEventListener("click",this._customAlert) ;
}
render() {
return (
<div>
<button>自定义事件</button>
</div>);
}
_customAlert(e){
alert("custom event") ;
}
}
以上只是一个例子而已,当然对于按钮来说是有 onClick 事件的。
2、原生事件一定要手动移除
对于原生事件,我们一定要手动移除,否则很可能出现内存泄露的问题,一般情况下,我们在 componentDidMount() 方法中注册原生事件,在 componentWillUnmount() 方法中移除事件。而使用合成事件,则不需要,因为 React 内部已经帮我们处理好了。
3、合成事件和原生事件
对合成事件和原生事件,我们尽量不要合成事件和原生事件混用,只是对于无法使用 React 合成事件解决问题的这一场景,我们才需要去使用原生事件。
1、原生事件的事件冒泡和事件捕获
先来一张图来看看 js 冒泡事件和捕获原型
- 事件捕获
事件捕获就相当于拿个鱼叉捕鱼一样,从水面上面举起叉子插入到水面水鱼所在的位置,在原生 JS 中,就是从 html 所外层的元素向内层元素触发。
- 事件冒泡:
事件冒泡和事件捕获刚好相反,就想当鱼从水泡吹出一个泡泡到水面上,是从 html 内层元素到外层元素触发的。
- 举个栗子!
有一个 div 里面有个 button
<div>
<button>点我</button>
</div>
假设这两个元素都绑定了 onclick 事件,如果用户点击了 button ,它在 div 和 p 上都触发了 click 事件,那么执行事件的顺序是如何呢?
事件冒泡:button [子元素先触发] 先触发事件,再到 div
事件捕获:div 先触发事件,再到 button
但是在不同的浏览器中处理是不同的,IE 浏览器仅支持事件冒泡,Netscape 支持事件冒泡。
开发人员可以自己选择绑定事件采用冒泡还是捕获,可以通过方法 addEventListener 的第三个参数来指定。
el.addEventListener('someEvent',doSomething,true)
第三个参数如果为 true 就是事件捕获,如果是 false 就是事件冒泡。
但是 IE 浏览器除外( IE 只支持事件冒泡),也不支持 addEventListener 函数,他有一个自己的函数叫 el.attachEvent("onclick", doSomething);
2、事件传播是可以阻止的(原生事件)
(1)、禁止事件冒泡
- 1、在 W3C 中,使用 stopPropagation() 方法。
- 2、在 IE 下,设置 window.event.cancelBubble = true。
(2)、禁止默认事件
1、阻止默认浏览器动作(W3C)使用, e.preventDefault()。
2、IE中阻止函数器默认动作的方式 , window.event.returnValue = false。
具体可以看这篇文章
3、注意点
- 1、阻止 React 合成事件冒泡,并不能阻止原生事件的冒泡,就算使用 stopPropagation 也无法阻止原生事件的冒泡。
- 2、取消原生事件的冒泡也会同时取消 React 事件,并且原生事件的冒泡在 React 事件的触发和冒泡之前。
以上就是 React 的事件系统,信息量还是比较大的,希望大家多多实践。
Demo 地址: https://github.com/tigerchain/react-lesson/tree/master/lesson03/12-eventsys
据说点喜欢的人都能成为大神。