淺談JS事件機制與React事件機制

original_galaxy發表於2018-08-17

在JavaScript中,事件的觸發實質上是要經過三個階段:

  • 首先事件捕獲階段:事件由父元素一直傳遞到事件發生的元素
  • 執行目標物件本身的事件處理
  • 然後事件冒泡:事件從子元素向父元素冒泡

正因為事件在DOM的傳遞經歷這樣過程,為行為委託提供了可能,即行為委託的實質就是將子元素事件的處理委託給父級元素處理。

  • React事件並沒有原生的繫結在真實的DOM上,而是使用了行為委託方式實現事件機制,將事件最終都委託到最外層document,統一監聽,在冒泡階段處理,當掛載或者解除安裝元件時,在統一的事件監聽位置增加或者刪除物件。
  • React並沒有使用原生的瀏覽器事件,而是在基於Virtual DOM的基礎上實現了合成事件(SyntheticEvent),事件處理程式接收到的是SyntheticEvent的例項。
  • SyntheticEvent完全符合W3C的標準,因此在事件層次上具有瀏覽器相容性,與原生的瀏覽器事件一樣擁有同樣的介面,可以通過stopPropagation()和preventDefault()相應的中斷。如果需要訪問當原生的事件物件,可以通過引用nativeEvent獲得。

另外注意不要將React事件和DOM原生事件混用,譬如常見的sidebar的點選外部即隱藏的實現,阻止事件冒泡時,就要在真實DOM層面中去stopPropagation:

componentDidMount() {
  // 點選body後隱藏sidebar
  document.body.addEventListener('click', e => {
    this.setState({
      showSidebar: false,
    });
  });
  
  // 點選sidebar本身時,阻止click事件冒泡到body,則sidebar不會隱藏
  document.querySelector('.sidebar').addEventListener('click', e => {
    e.stopPropagation();
  })
}

componentWillUnmount() {
  document.body.removeEventListener('click');
  document.querySelector('.sidebar').removeEventListener('click');
}
複製程式碼

或者通過原生事件物件中的target進行條件判斷:

componentDidMount() {
  document.body.addEventListener('click', e => {
    if (e.target && e.target.matches('div.sidebar')) {
      return;
    }
    
    this.setState({
      showSidebar: false,
    });
  });
}
複製程式碼

相關文章