在開始之前宗侦,可以先看一下我的另一篇關(guān)于dom本身的事件機(jī)制《談?wù)刯s點(diǎn)擊之后發(fā)生了什么》
前言
????如果你有試過(guò)輸出react
事件中的event
,你就會(huì)發(fā)現(xiàn)這個(gè)event
好像和我們看到的dom
事件中的event
不太一樣嗅剖,那是因?yàn)閞eact在進(jìn)行dom事件綁定時(shí)辩越,不是直接綁定事件的,而是通過(guò)所謂的合成事件(SyntheticEvent
)進(jìn)行委托管理的信粮,它是原生事件進(jìn)行封裝后的結(jié)果黔攒,你可以通過(guò)nativeEvent
獲取原生事件。
通過(guò)例子觀察
class App extends Component {
componentDidMount(){
document.addEventListener('click', function(){
console.log('document click')
})
document.getElementsByClassName('App')[0].addEventListener('click', function(){
console.log('app click')
})
document.getElementsByTagName('button')[0].addEventListener('click', function(e){
console.log('button click')
// e.stopPropagation();
})
}
onClick = (e) => {
e.stopPropagation() // 能夠阻止div.app的觸發(fā)
e.nativeEvent.stopImmediatePropagation(); // 能夠阻止document的觸發(fā)
e.nativeEvent.stopPropagation(); // 什么都阻止不了
console.log('react button click');
};
render() {
return (
<div className="App" onClick={() => {console.log('react app click')}}>
<button onClick={this.onClick}>按鈕</button>
</div>
);
}
}
我們?cè)谟胷eact綁定了兩個(gè)事件,同時(shí)在didmount
給真實(shí)dom也綁定了事件督惰,點(diǎn)擊button之后的執(zhí)行順序是
button click
app click
react button click
react app click
document click
原理
react并不是我之前所設(shè)想的將事件綁定在真實(shí)dom上不傅,而是通過(guò)自己的事件處理器來(lái)處理,將所有的事件都綁定在document上赏胚,這樣真實(shí)點(diǎn)擊的時(shí)候访娶,冒泡到document上,react再通過(guò)document去dispatchEvent統(tǒng)一處理事件
所以上面的
stopPropagation
就能理解了觉阅,e.stopPropagation
只能阻止虛擬dom的事件冒泡崖疤,但它本身是由document觸發(fā)的,所以e.nativeEvent.stopPropagation
什么也阻止不了典勇,document
就是冒泡的頂點(diǎn)劫哼,e.nativeEvent.stopImmediatePropagation
可以阻止document
的事件,這和它本身有關(guān)
需要注意的是react
在生成真實(shí)的dom節(jié)點(diǎn)會(huì)加入一些東西幫助事件分發(fā)
dom節(jié)點(diǎn)的react屬性(15和16有點(diǎn)區(qū)別)
15和16會(huì)有一點(diǎn)細(xì)微的區(qū)別割笙,具體什么還沒有去了解权烧,但是看來(lái)對(duì)事件的影響不大,15是有一個(gè)__rootNodeID
來(lái)區(qū)分組件伤溉,16是通過(guò)__debugID
來(lái)區(qū)分般码,如果理解上有錯(cuò)誤,歡迎指正哦
- vdom
// 簡(jiǎn)化
let vdom = {
type: 'div',
props: {
onClick: function(){
console.log('react app click')
},
children: [
{
type: 'button',
props: {
onClick: function() {
console.log('react button click')
}
}
}
]
}
}
- 注冊(cè)事件
let bankForRegistrationName = {}; // 回調(diào)事件的保存
// react構(gòu)建真實(shí)dom樹
...
// 注冊(cè)事件
bankForRegistrationName = {
// 數(shù)字是_debugID,react用于識(shí)別每一個(gè)dom
5: {
click: function(){
console.log('react app click')
},
},
6: {
click: function(){
console.log('react button click')
},
}
}
- 事件觸發(fā)
// 合成事件簡(jiǎn)單實(shí)現(xiàn)
function SyntheticEvent(e) {
...
this.nativeEvent = e;
...
}
// e: event乱顾, type: 事件類型
function dispatchEvent(e, type) {
let synE = new SyntheticEvent(e);
// 執(zhí)行監(jiān)聽事件
let debugID = e.target.__reactInternalInstance$om8tco7dvl._debugID;
bankForRegistrationName[debugID][type](synE);
}
// document事件委托
document.addEventListener('click', function(e) {
dispatchEvent(e, 'click');
})
總結(jié)
????基本上就是這樣了(當(dāng)然沒有對(duì)冒泡做處理侈询,react會(huì)遍歷自己的vdom去執(zhí)行冒泡
)
- 事件管理中心(
bankForRegistrationName
)會(huì)在react-render
過(guò)程中保存所有所有dom事件
- document作為事件委托者,用來(lái)分發(fā)事件(
dispatchEvent
),通過(guò)dom節(jié)點(diǎn)唯一標(biāo)識(shí)(_debugID
)去事件管理(bankForRegistrationName
)觸發(fā)事件