15、React系列之--React 事件系統(tǒng)

版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。

PS:轉(zhuǎn)載請注明出處
作者:TigerChain
地址:http://www.itdecent.cn/p/99dc37f9edf3
本文出自TigerChain簡書

React 教程系列

教程簡介

  • 1、閱讀對象

本篇教程適合初學者,老鳥直接略過,如果有誤,歡迎指出,謝謝。

eventsys.gif

一、事件和事件系統(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 冒泡事件和捕獲原型

jsevent.png
  • 事件捕獲

事件捕獲就相當于拿個魚叉捕魚一樣,從水面上面舉起叉子插入到水面水魚所在的位置,在原生 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ù)說點喜歡的人都能成為大神。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,058評論 25 709
  • 原教程內(nèi)容詳見精益 React 學習指南,這只是我在學習過程中的一些閱讀筆記,個人覺得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,944評論 1 18
  • 本文通過一個簡短的實例&控制臺調(diào)試,了解react事件處理的全過程。下面是測試用代碼,使用控制臺可以清晰看到函數(shù)執(zhí)...
    溪離欣洛閱讀 1,282評論 2 2
  • 雨中東湖七山連縱 本來一個月前約好十二人的隊伍,因種種,只剩我母女倆赴約。前一天臨睡前,米爸還要求我們別去了。沒應...
    Friz閱讀 300評論 0 0
  • 舟行兩廣地,夜泊銀沙灘。聞鐘雞冠寺,潮汐遲遲歸。
    好客坊閱讀 284評論 1 0

友情鏈接更多精彩內(nèi)容