版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。
PS:轉(zhuǎn)載請注明出處
作者:TigerChain
地址:http://www.itdecent.cn/p/99dc37f9edf3
本文出自TigerChain簡書
教程簡介
- 1、閱讀對象
本篇教程適合初學者,老鳥直接略過,如果有誤,歡迎指出,謝謝。
-
2、教程難度
初級
3、Demo 地址:https://github.com/tigerchain/react-lesson/tree/master/lesson03/12-eventsys
4、本節(jié) demo 效果圖如下:

一、事件和事件系統(tǒng)?
1、什么事件?
事件是一個宏觀的概念,在各個領域中含義也不太一樣。一般來說 事件就是:比較重大,對一定的人群會產(chǎn)生一定影響的事情。
但是在編程中的事件,是一個抽像的概念,可以理解就是干什么(動作),比如:點擊一個按鈕是一個事件,刷新網(wǎng)頁是一個事件,拖拽,移動等等都是是事件。
事件不能單獨工作,事件和事件源,監(jiān)聽器三者聯(lián)合起來才可以工作,所以我們也可以說,事件的三劍客就是,事件、事件源和監(jiān)聽器。
舉個栗子?。。?/p>
比如點擊一個按鈕顯示出一句話:" Hello React " ! ,那么分析一下這一事件的過程。
事件:點擊
事件源:按鈕(發(fā)生事件的對象也叫事件的產(chǎn)生器)
監(jiān)聽器:監(jiān)聽著事件處理結(jié)果,一般是一個回調(diào)函數(shù)。
2、什么是事件系統(tǒng)?
軟件開發(fā)中的事件系統(tǒng)也就是事件處理系統(tǒng),它包含事件監(jiān)聽,事件分發(fā),事件處理回調(diào)等等一系列過程。(個人理解,如有不同理解可以指出)。每個開發(fā)平臺有自己不同的事件處理機制。
二、React 事件系統(tǒng)
1、跨瀏覽器使用
React 標準化了事件對象,和瀏覽器本地事件是是同一個接口( React 也就是把瀏覽器的事件包裝了一把而已),所以它可以工作在所有的瀏覽器中,可以直接在所有的瀏覽器使用 React 的事件。
2、React 的事件分類
1、合成事件
一、React 事件和 HTML 事件
在 React 中綁定事件和 HTML 中綁定事件類似,但是還是有以下不同。
- 1、React 事件的命名是駝峰標志,比如: onClick 而不能是 onclick。
- 2、在 JSX 中你可以傳一個方法去處理函數(shù),而不是一個字符串。
- 3、React事件并沒有原生的綁定在真實的DOM上,而是使用了 行為委托 方式實現(xiàn)事件機制。
舉個例子
在 HTML 中一個按鈕的點擊事件
<button onclick="showImg()">顯示圖片</button>
在 React 中一個按鈕的點擊事件
<button onClick={showImg}>顯示圖片</button>
PS:
關于 HTML 事件可以看這里:http://www.runoob.com/jsref/dom-obj-all.html
二、合成事件
用官網(wǎng)的話說:React 實現(xiàn)了一個“合成事件”層(synthetic event system),這個事件模型保證了和 W3C 標準保持一致,所以不用擔心有什么詭異的用法,并且這個事件層消除了 IE 與 W3C 標準實現(xiàn)之間的兼容問題。
綁定事件,在 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("組件綁定事件實現(xiàn)方法") ;
}
}
PS:每次點擊的時候都要重新綁定一個函數(shù),我們知道函數(shù)的創(chuàng)建和銷毀是需要開銷的,所以這種方式對性能有影響。
- 2、構(gòu)造方法中綁定。
格式:
constructor(props){
super(props) ;
this.方法 = this.方法.bind(this,'event','args') ;
// event(事件名) 和 args(參數(shù)) 不是必須的。
}
<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("構(gòu)造方法綁定事件實現(xiàn)方法") ;
}
}
使用構(gòu)造方法綁定事件,只需要綁定一次。而不用每次使用的時候都去綁定。
- 3、使用箭頭函數(shù)。
格式:
<Component 事件={(e) => this.方法(e,args)}</Component>
//其中 args (參數(shù))不是必須的。
實例
import React ,{Component} from 'react';
class EventApp extends Component {
constructor(props){
super(props) ;
}
render(){
return
(<div>
<button onClick={(e) => this._clickMe(e,"使用箭頭函數(shù)綁定")}>使用箭頭函數(shù)綁定事件</button> <p/>
</div>
) ;
}
_clickMe(e,args){
alert("箭頭函數(shù)綁定事件實現(xiàn)方法") ;
alert(args);
alert(e);
}
}
PS:使用箭頭函數(shù)系統(tǒng)就默認把 this 綁定到了 _clickMe 方法中,但是需要注意一點,由于箭頭函數(shù)綁定是定義在 redner 方法中的,組件的每一次渲染都會創(chuàng)建一個新的箭頭函數(shù) [ React 中渲染是經(jīng)常會發(fā)生的]。這種方式和組件上綁定是一個道理,對性能有影響。
總結(jié): 綜上所述,我個人建議使用在構(gòu)造方法中綁定事件的方法這種方式。
三、合成事件綁定事件的處理函數(shù)
React 合成事件綁定事件處理的函數(shù)涉及到鼠標類,觸摸類,鍵盤類,表單和焦點等。
具體可以看官方 Supported Events 這一小節(jié): https://facebook.github.io/react/docs/events.html,這沒有什么好說的。
注意:經(jīng)過上面的分析,我們知道 React 的事件綁定有三種方式,但是我們要注意一點,我們不能直接在組件中監(jiān)聽事件。因為組件是 DOM 元素的包裝器,如果在組件中直接監(jiān)聽事件,那么到底事件應該是對應那個 DOM 元素的,是一個 button 的還是一張圖片的,你就分不清楚了。這樣你也分不清楚它和 props 有啥區(qū)別。
比如:定義一個含有按鈕的子組件 ShowPlus.js 。然后在父組件中監(jiān)聽事件。
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") ;
}
}
以上方式是錯誤的,你把按鈕點死它也不會調(diào)用的,所以我們不要直接在一個自定義組件上添加監(jiān)聽器。
那么我們?nèi)绾翁幚磉@種情況呢,看過 props 這一節(jié)的朋友很快就會有答案的,我們可以把事件處理器當作 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 傳遞給子組件,然后在組件內(nèi)部,我們把事件監(jiān)聽到一個 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 不能識別的事件,我們就要使用原生傳統(tǒng)的事件方式 addEventListener 處理。修改上面的代碼。
正確的例子:
import React, { Component, PropTypes } from 'react';
/**
* 自定義事件
*/
export default class CustomEvent extends Component {
constructor(props) {
super(props);
}
// 組件掛載之后注冊監(jiān)聽器
componentDidMount(){
window.addEventListener("click", this._customAlert);
}
//組件卸載之后移除監(jiān)聽器
componentWillUnmount(){
window.removeEventListener("click",this._customAlert) ;
}
render() {
return (
<div>
<button>自定義事件</button>
</div>);
}
_customAlert(e){
alert("custom event") ;
}
}
以上只是一個例子而已,當然對于按鈕來說是有 onClick 事件的。
2、原生事件一定要手動移除
對于原生事件,我們一定要手動移除,否則很可能出現(xiàn)內(nèi)存泄露的問題,一般情況下,我們在 componentDidMount() 方法中注冊原生事件,在 componentWillUnmount() 方法中移除事件。而使用合成事件,則不需要,因為 React 內(nèi)部已經(jīng)幫我們處理好了。
3、合成事件和原生事件
對合成事件和原生事件,我們盡量不要合成事件和原生事件混用,只是對于無法使用 React 合成事件解決問題的這一場景,我們才需要去使用原生事件。
1、原生事件的事件冒泡和事件捕獲
先來一張圖來看看 js 冒泡事件和捕獲原型

- 事件捕獲
事件捕獲就相當于拿個魚叉捕魚一樣,從水面上面舉起叉子插入到水面水魚所在的位置,在原生 JS 中,就是從 html 所外層的元素向內(nèi)層元素觸發(fā)。
- 事件冒泡:
事件冒泡和事件捕獲剛好相反,就想當魚從水泡吹出一個泡泡到水面上,是從 html 內(nèi)層元素到外層元素觸發(fā)的。
- 舉個栗子!
有一個 div 里面有個 button
<div>
<button>點我</button>
</div>
假設這兩個元素都綁定了 onclick 事件,如果用戶點擊了 button ,它在 div 和 p 上都觸發(fā)了 click 事件,那么執(zhí)行事件的順序是如何呢?
事件冒泡:button [子元素先觸發(fā)] 先觸發(fā)事件,再到 div
事件捕獲:div 先觸發(fā)事件,再到 button
但是在不同的瀏覽器中處理是不同的,IE 瀏覽器僅支持事件冒泡,Netscape 支持事件冒泡。
開發(fā)人員可以自己選擇綁定事件采用冒泡還是捕獲,可以通過方法 addEventListener 的第三個參數(shù)來指定。
el.addEventListener('someEvent',doSomething,true)
第三個參數(shù)如果為 true 就是事件捕獲,如果是 false 就是事件冒泡。
但是 IE 瀏覽器除外( IE 只支持事件冒泡),也不支持 addEventListener 函數(shù),他有一個自己的函數(shù)叫 el.attachEvent("onclick", doSomething);
2、事件傳播是可以阻止的(原生事件)
(1)、禁止事件冒泡
- 1、在 W3C 中,使用 stopPropagation() 方法。
- 2、在 IE 下,設置 window.event.cancelBubble = true。
(2)、禁止默認事件
1、阻止默認瀏覽器動作(W3C)使用, e.preventDefault()。
2、IE中阻止函數(shù)器默認動作的方式 , window.event.returnValue = false。
具體可以看這篇文章
3、注意點
- 1、阻止 React 合成事件冒泡,并不能阻止原生事件的冒泡,就算使用 stopPropagation 也無法阻止原生事件的冒泡。
- 2、取消原生事件的冒泡也會同時取消 React 事件,并且原生事件的冒泡在 React 事件的觸發(fā)和冒泡之前。
以上就是 React 的事件系統(tǒng),信息量還是比較大的,希望大家多多實踐。
Demo 地址: https://github.com/tigerchain/react-lesson/tree/master/lesson03/12-eventsys
據(jù)說點喜歡的人都能成為大神。