摘要:綁定事件處理函數(shù)指向的四中方式以及他們的優(yōu)缺點。內部自己實現(xiàn)了一套高效的事件機制,為了提高框架的性能,通過事件冒泡,只在節(jié)點上注冊原生的事件,內部自己管理所有組件的事件處理函數(shù),以及事件的冒泡捕獲。
前面的文章介紹了 React 的 JSX 語法、組件的創(chuàng)建方式、組件的屬性、組件的內部狀態(tài)以及組件的生命周期。另外,還順帶說了各個知識點要重點注意的事情,以及我在項目實踐中的一些經(jīng)驗。如果你覺得對自己有幫助,可以通過 玩轉 React(一)- 前言 中的文章目錄進行閱讀。
另外,為了方便大家更好地交流 React、分享前端開發(fā)經(jīng)驗,我建了一個微信群,由于微信群二維碼有時間限制,你可以先加我好友(我的微信:leobaba88),驗證信息 玩轉 React,我會拉你入群,歡迎大家,下面是我的微信二維碼。
好的,言歸正傳,今天我們說一下在 React 中是如何處理事件的。事件處理是前端開發(fā)過程中非常重要的一部分,通過事件處理機制,我們的前端應用可以響應用戶的各種操作,從而實現(xiàn)一個富交互的前端應用。
內容摘要如何為 React 的內置組件設置事件處理函數(shù)。
React 事件對象與瀏覽器原生 DOM 事件對象的區(qū)別。
默認情況下不能以異步的方式使用事件對象,如在 setTimeout 中。
不要在組件中使用 addEventListener 注冊事件處理函數(shù),有坑。
綁定事件處理函數(shù) this 指向的四中方式以及他們的優(yōu)缺點。
React 內置組件的事件處理我所說的 React 內置組件是指 React 中已經(jīng)定義好的,可以直接使用的如 div、button、input 等與原生 HTML 標簽對應的組件。
我們先回顧一下瀏覽器原生 DOM 上注冊事件的方式。
第一種方式:
Click me.
這是一種古老的方式,在 DOM level 1 規(guī)范中的事件注冊方式,現(xiàn)在已經(jīng)很少使用了。
這種方式,用來注冊事件的 HTML 屬性的值是一個字符串,是一段需要執(zhí)行的 JavaScript 代碼。
可以通過 return false; 來阻止當前 HMTL 元素的默認行為,如 a 標簽的頁面跳轉。
關于 DOM 規(guī)范的級別可以參考:DOM Levels
第二種方式:
Click me.
這是 DOM level 2 規(guī)范中引入的事件注冊方式,目前各瀏覽器也支持的很好,用得是最多的,就是寫起來有點啰嗦哈。
在 React 中,事件注冊與方式一非常類似,不過有如下幾點不同:
屬性名稱采用駝峰式(如:onClick,onKeyDown),而不是全小寫字母。
屬性值接受一個函數(shù),而不是字符串。
return false; 不會阻止組件的默認行為,需要調用 e.preventDefault();
如下所示:
function ActionLink() { function handleClick(e) { e.preventDefault(); console.log("The link was clicked."); } return ( Click me ); }
這是一個以函數(shù)方式定義的組件,組件渲染一個 a 元素,設置l鏈接的點擊事件,通過事件處理函數(shù)接收到的事件對象(e),阻止了鏈接的默認行為,并打印 "The link was clicked." 到控制臺上。設置 React 內置組件的事件處理函數(shù)是不是非常簡單。
React 事件對象 VS 原生的 DOM 事件對象React 中的事件對象稱之為 SyntheticEvent(合成對象),它是依據(jù) DOM Level 3 的事件規(guī)范實現(xiàn)的,這樣做最大的好處是可以屏蔽瀏覽器的差異,各種廠商的瀏覽器對規(guī)范的實現(xiàn)程度是不一樣的,如果直接使用原生 DOM 事件對象的話,有些情況下你需要考慮瀏覽器的兼容性。而 React 通過 SyntheticEvent 已經(jīng)把這些瑣事幫你搞定了,在任何 React 支持的瀏覽器下,事件對象都有一致的接口。
React 中所有的事件處理函數(shù)都會接收到一個 SyntheticEvent 的實例 e 作為參數(shù),如果在某些特殊的場景中,你需要用到原生的 DOM 事件對象,可以通過 e.nativeEvent 來獲取。
不要在異步過程中使用 React 事件對象需要說明的是,出于性能的考慮,React 并不是為每一個事件處理函數(shù)生成一個全新的事件對象,事件對象會被復用,當事件處理函數(shù)被執(zhí)行以后,事件對象的所有屬性會被設置為 null,所以在事件處理函數(shù)中,你不能以異步的方式使用 React 的事件對象,因為那時候事件對象的所有屬性都是 null 了,或者已經(jīng)不是你關心的那個事件了。
盡量不要使用 addEventListener這里稍微深入一下,不然我怕有的同學會踩坑。React 內部自己實現(xiàn)了一套高效的事件機制,為了提高框架的性能,React 通過 DOM 事件冒泡,只在 document 節(jié)點上注冊原生的 DOM 事件,React 內部自己管理所有組件的事件處理函數(shù),以及事件的冒泡、捕獲。
所以說,如果你通過 addEventListener 注冊了某個 DOM 節(jié)點的某事件處理函數(shù),并且通過 e.stopPropagation(); 阻斷了事件的冒泡或者捕獲,那么該節(jié)點下的所有節(jié)點上,同類型的 React 事件處理函數(shù)都會失效。
如下示例,雖然設置的鏈接的點擊事件,但是它卻執(zhí)行不了。
class CounterLink extends React.Component { constructor(props) { super(props); this.state = { count: 0 } this.handleClick = this.handleClick.bind(this); } componentDidMount() { document.querySelector(".my-link").addEventListener("click", (e) => { console.info("raw click"); e.stopPropagation(); }) } handleClick(e) { e.preventDefault(); console.info("react click"); this.setState({ count: this.state.count + 1 }); } render() { return ( ) } } ReactDOM.render(, document.querySelector("#root"));
https://codepen.io/Sarike/pen...
如何綁定事件處理函數(shù)的 this在以類繼承的方式定義的組件中,為了能方便地調用當前組件的其他成員方法或屬性(如:this.state),通常需要將事件處理函數(shù)運行時的 this 指向當前組件實例。
如下面的示例:
class Link extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } handleClick(e) { e.preventDefault(); this.setState({ count: this.state.count + 1 }) } render() { return Clicked me {this.state.count} times. } } ReactDOM.render(, document.querySelector("#root"))
當點擊鏈接時,控制臺會報錯:Uncaught TypeError: Cannot read property "setState" of undefined,就是因為沒有將 handleClick 運行時的 this 綁定到當前組件。
綁定事件處理函數(shù)的 this 到當前組件,有如下幾種方式。
第一種方式,通過 bind 方法,原地綁定事件處理函數(shù)的 this 指向,如下所示:
Clicked me {this.state.count} times.
這種方式的優(yōu)點是書寫起來相對簡單,但是每次渲染都會執(zhí)行 bind 方法生成一個新的函數(shù),會有額外的開銷,由于事件處理函數(shù)是作為屬性傳遞的,所以從而導致子組件進行重新渲染,顯然這不是一種好的方式。
第二種方式,通過一個箭頭函數(shù)將真實的事件處理函數(shù)包裝一下,如下所示:
this.handleClick(e)}> Clicked me {this.state.count} times.
這種方式書寫起來也不算麻煩,不過也沒有解決第一種方式面臨的性能開銷和重新渲染的問題。但是這種方式的一個好處是能清晰描述事件處理函數(shù)接收的參數(shù)列表(這一點可能因人而異,個人觀點覺得這是一個優(yōu)點)。
第三種方式,在 constructor 中預先將所有的事件處理函數(shù)通過 bind 方法進行綁定。如下所示:
class Link extends React.Component { constructor(props) { super(props); this.state = { count: 0 } // 重點在這里 this.handleClick = this.handleClick.bind(this); } handleClick(e) { e.preventDefault(); this.setState({ count: this.state.count + 1 }) } render() { return Clicked me {this.state.count} times. } } ReactDOM.render(, document.querySelector("#root"))
這種方式能解決前兩種方式面臨的額外開銷和重新渲染的問題,但是寫起來略微有點復雜,因為一個事件處理函數(shù)要分別在三個不同的地方進行定義、綁定 this 和使用。
第四種方式,使用類的成員字段定義語法,如下所示:
class Link extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } handleClick = e => { e.preventDefault(); this.setState({ count: this.state.count + 1 }) } render() { return Clicked me {this.state.count} times. } } ReactDOM.render(, document.querySelector("#root"))
這種方式解決了上面三種方式面臨的性能開銷、重新渲染以及書寫麻煩的問題。唯一的問題就是這種語法目前處于 Stage 3,還未納入到正式的 ES 規(guī)范中。參考:https://github.com/tc39/propo...
不過這也沒太大關系。
總結本文的內容并不多,可能說的有點啰嗦。簡單總結一下,React 中通過設置組件的 事件屬性 來注冊事件,React 內部自己實現(xiàn)了一套包含冒泡、捕獲邏輯在內的事件機制,所以盡量不要使用 addEventListener,除非你知道自己在干什么。有四種為事件處理函數(shù)綁定 this 的方法,推薦使用類屬性定義的方式來定義處理函數(shù),如果你不太在意哪一點性能開銷的話,可以使用箭頭函數(shù)包裝真實事件回調的方式。另外,事件對象在 React 中是被復用的,事件回調被執(zhí)行以后,事件對象的所有屬性會被重置為 null,所以不要在異步的過程中使用事件對象。
好了,有什么疑問可以加微信群交流,我的微信號:leobaba88,驗證信息:玩轉 React。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/89549.html
摘要:本人計劃編寫一個針對中初級前端開發(fā)者學習的系列教程玩轉。使用的原因是新的語言規(guī)范開發(fā)效率更高代碼更優(yōu)雅,尤其是基于開發(fā)的項目。其次也是目前特別流行的一個前端框架,截止目前,上有將近萬,國內一二線互聯(lián)網(wǎng)公司都有深度依賴開發(fā)的項目。 本人計劃編寫一個針對中初級前端開發(fā)者學習 React 的系列教程 - 《玩轉 React》。 文章更新頻率:每周 1 ~ 2 篇。 目錄 玩轉 React(...
摘要:函數(shù)屬性或者說事件在組件之間通信過程中是必不可少的,但是切莫讓它影響了大家對單向數(shù)據(jù)流這一概念的理解。這應該屬于一種的使用方式,而且這樣做有悖單向數(shù)據(jù)流原則。 上一篇文章 玩轉 React(六)- 處理事件 介紹了在 React 中如何處理用戶事件,以及 React 事件機制與原生 DOM 事件的差異和注意的問題,同時也介紹了事件處理函數(shù)中 this 的指向問題以及處理的幾種方式及其優(yōu)...
摘要:另外本文中會介紹一個通過類繼承方式定義的組件的生命周期,以及在各個生命周期函數(shù)中能做什么,不能或盡量不要做什么。各個生命周期函數(shù)介紹及使用經(jīng)驗。獲取組件的初始內部狀態(tài)在中。該聲明周期函數(shù)可能在兩種情況下被調用組件接收到了新的屬性。 文章標題總算是可以正常一點了…… 通過之前的文章我們已經(jīng)知道:在 React 體系中所謂的 在 JavaScript 中編寫 HTML 代碼 指的是 Rea...
摘要:另外本文中會介紹一個通過類繼承方式定義的組件的生命周期,以及在各個生命周期函數(shù)中能做什么,不能或盡量不要做什么。各個生命周期函數(shù)介紹及使用經(jīng)驗。獲取組件的初始內部狀態(tài)在中。該聲明周期函數(shù)可能在兩種情況下被調用組件接收到了新的屬性。 文章標題總算是可以正常一點了…… 通過之前的文章我們已經(jīng)知道:在 React 體系中所謂的 在 JavaScript 中編寫 HTML 代碼 指的是 Rea...
摘要:后面將會仔細分析的源碼實現(xiàn)。更新完成后,對中的每個元素執(zhí)行動畫的邏輯,對中的每個元素執(zhí)行動畫的邏輯。事實上,原因很簡單,事件在某些情況是不會被觸發(fā)??偨Y動畫是組件初次后,才會被添加到的所有子元素上。參考資料官方文檔事件 過去一年,React 給整個前端界帶來了一種新的開發(fā)方式,我們拋棄了無所不能的 DOM 操作。對于 React 實現(xiàn)動畫這個命題,DOM 操作已經(jīng)是一條死路,而 CSS...
閱讀 2051·2021-11-23 09:51
閱讀 932·2021-11-19 09:40
閱讀 889·2021-10-27 14:20
閱讀 5228·2021-10-09 09:52
閱讀 3369·2021-10-09 09:44
閱讀 1786·2021-10-08 10:05
閱讀 5298·2021-09-09 11:47
閱讀 3552·2019-08-30 12:47