事件系統(tǒng)
之前了解過虛擬DOM在內(nèi)存中是以對象的形式存在坎背,那么給對象添加事件就會比較方便缓呛。React基于虛擬DOM實現(xiàn)了一種合成事件,我們定義的所有事件處理器都會接收到一個事件對象的實例。
所有的事件都是綁定到最外層捎泻,如果需要訪問原生的事件對象,可以使用nativeEvent屬性埋哟。
- 合成事件的綁定方式
<button onClick={this.handle.bind(this)}>button</button>
React并不會像DOM0級事件那樣將事件處理器直接綁定到HTML原生上笆豁,這里是一個函數(shù)指針。
- 合成事件的實現(xiàn)機制
在React中赤赊,對合成事件做了兩件事情:事件委托和自動綁定渔呵。
1.事件委托
在React中,并不會把事件函數(shù)綁定到真實的節(jié)點上砍鸠,而是把所有的事件綁定到結(jié)構(gòu)的最外層扩氢,使用一個統(tǒng)一的事件監(jiān)聽器。該事件監(jiān)聽器維持了一個映射來保存所有組件內(nèi)部的事件監(jiān)聽爷辱。當組件的掛載和卸載的操作录豺,只是在該統(tǒng)一的事件監(jiān)聽器中刪除一寫對象。當事件發(fā)生時饭弓,首先被該事件監(jiān)聽器處理双饥,然后在該映射中找到真正的事件處理函數(shù)并調(diào)用。
2.自動綁定
React中每個方法的上下文都會指向該組件的實例弟断,即自動為this綁定到當前的組件咏花。而且React還會對這種綁定進行緩存。如果使用es6的方式創(chuàng)建組件阀趴,那么這種自動綁定不會存在昏翰,這樣我們就需要手動進行綁定。
import {Component} from 'react'
class App extends Component {
constructor(props) {
super(props)
}
handle() {
}
render() {
return (
<button disabled onClick={this.handle.bind(this)}></button>
)
}
}
我們還可以在構(gòu)造器內(nèi)完成this的綁定刘急,這樣就不用每次調(diào)用事件監(jiān)聽器的時候都要進行綁定棚菊。
import {Component} from 'react'
class App extends Component {
constructor(props) {
super(props)
this.handle = this.handle.bind(this) //構(gòu)造器中完成
}
handle() {
}
render() {
return (
<button disabled onClick={this.handle}></button>
)
}
}
我們還可以通過箭頭函數(shù),它會自動綁定此函數(shù)作用域的this叔汁,因此我們不需要對他使用bind方法统求。
- 在React中使用原生事件
在React的生命周期中检碗,在componentDidMount方法中完成組件的加載和渲染,存在真實的DOM码邻,此時我們可以完成事件的綁定折剃。
注意,在React中使用DOM原生事件的時候像屋,我們一定要在組件卸載的時候手動一處微驶,否則會出現(xiàn)內(nèi)存泄漏的問題,通過合成事件系統(tǒng)則不需要开睡,React內(nèi)部會幫我們進行處理因苹。
- 合成事件和原生事件混用
之前做了一個項目,實現(xiàn)一個下拉框篇恒,點擊下拉框的時候下拉框顯示扶檐,但是點擊屏幕其他部分的時候下拉框收起,如果我們給document綁定一個點擊事件胁艰,點擊的時候下拉框收起款筑。但是發(fā)現(xiàn)一個問題,如果這樣實現(xiàn)的話腾么,點擊下拉框的時候奈梳,下拉框依然會是收起狀態(tài),不會顯示解虱。
這是因為攘须,React合成事件系統(tǒng)的委托機制,在合成事件中僅僅是在最外層的容器進行了綁定殴泰,并且依賴事件的冒泡機制完成了委托于宙。所以我在點擊出現(xiàn)下拉框中的使用e.preventDefault()并沒有什么作用,因為在合成事件中該事件并不是綁定到了當前這個dom上悍汛,當前dom的事件只是對其的一個引用捞魁。
所以,我們要注意:
- 不要將合成事件和原生事件混用离咐。
- 可以通過e.target進行判斷谱俭。例如當前的事件元素是下拉框按鈕的話則return false。