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