一名中/高級前端工程師的自檢清單-React 篇
歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進階~
一、前言
? ? 你真的了解 React 嗎?我們在面試中往往涉及 React 時,第一個問題就是“解釋 React 是什么”。解釋一種技術(shù)是什么,在面試中也是非常常見的引起 話題的題目。本篇文章我就帶你掌握這一類概念題的解答技巧。
二、正文
一. 說說對 React 的理解,有哪些特性
官方的解釋:React 是一個 UI 庫,它的核心思想是UI=F(data), 即界面的呈現(xiàn)是由函數(shù)傳入的參數(shù)決定的
開發(fā)者不再需要關(guān)心界面是如何渲染的,只要關(guān)心數(shù)據(jù)的生成和傳遞,這大大提高的開發(fā)者的開發(fā)效率,節(jié)省了開發(fā)時間
其次 React 設(shè)計的
- 使用類似 HTML 的
JSX語法來描述視圖 - 通過
虛擬DOM修改真實DOM - 通過
setState修改數(shù)據(jù) - 在不同的
生命周期階段做不同的事 - 源碼底層對真實 DOM 事件進行封裝,使用
事件委托的方式來捕獲 DOM 事件 - ....
等特性進一步簡化的 真實 DOM 操作的復(fù)雜性
二. 說說真實 DOM 與虛擬 DOM 的區(qū)別,優(yōu)缺點
虛擬DOM2.1 虛擬 DOM 是什么
真實DOM就是我們在瀏覽器開發(fā)者工具中看到的DOM結(jié)構(gòu)虛擬DOM簡單來說就是 JS 對象,此對象中的字段包含了對真實DOM的描述:type:是什么標(biāo)簽/元素props:標(biāo)簽/元素有哪些屬性children:是否有子元素
a2.2 虛擬 DOM 大概是如何工作的
當(dāng)?DOM?操作(渲染更新)比較頻繁時,
React 底層會先將前后兩次的虛擬DOM?樹進行對比,
定位出具體需要更新的部分,生成一個補丁集,
最后只把“補丁”打在需要更新的那部分真實DOM?上,實現(xiàn)精準(zhǔn)的“差量更新”。
2.3 虛擬 DOM 的優(yōu)點
- 解決了頻繁操作真實 DOM 的低效率工作-不直接操作 DOM,數(shù)據(jù)驅(qū)動視圖,也在一定程度上提升了性能
- 解決了擴平臺開發(fā)的問題,因為虛擬 DOM 描述的東西可以是真實 DOM,也可以是安卓界面。IOS 界面等等,這就可以對接不同平臺的渲染邏輯。從而實現(xiàn)"一次編碼,多端運行"(如 React,React Native)
2.4 虛擬 DOM 的缺點
如果當(dāng)虛擬 DOM 的構(gòu)建和diff的過程相對復(fù)雜(比如很多遞歸遍歷等操作),那么虛擬 DOM 的 JS 計算是比較耗時的
三. 說說 Diff 算法
Diff算法一般的原始 diff 思路算法復(fù)雜度是O(n^3),即循環(huán)遞歸進行樹節(jié)點的一一對比
但 React 的?diff 算法是?O (n)?復(fù)雜度的思路
當(dāng)對比兩棵虛擬 DOM 樹時,React 首先比較兩棵樹的根節(jié)點。不同類型的根節(jié)點元素會有不同的形態(tài)
當(dāng)對比兩個相同類型的 React 元素時,React 會保留 DOM 節(jié)點,僅比對及更新有改變的屬性。
- 當(dāng)根節(jié)點為不同類型的元素時,React 會拆卸原有的樹并且建立起新的樹,這大大減少了?
Diff?過程中冗余的遞歸操作 - 當(dāng)對比兩個相同類型的 React 元素時,React 會保留 DOM 節(jié)點,僅比對及更新有改變的屬性
- 列表形式的子元素比較:React 引入了?
key 屬性。當(dāng)子元素?fù)碛?key?時,React 使用?key?來匹配原有樹上的子元素以及最新樹上的子元素,如果?key不同 不同則會拆卸原有的?key?節(jié)點并且建立起新的?key?節(jié)點
詳細(xì)內(nèi)容請參考React 官方文檔- Diffing 算法[2]
四. 說說 React 聲明周期有哪些不同階段,每個階段對應(yīng)的方法是什么
image.png4.1 創(chuàng)建階段
constructor():組件的構(gòu)造函數(shù),組件更新到界面上之前會先調(diào)用
- 用于初始化內(nèi)部狀態(tài),很少使用
- 唯一可以直接修改?
state?的地方
static getDerivedStateFromProps(nextProps, prevState):用于從外部的屬性去初始化一些內(nèi)部的狀態(tài)
- 當(dāng)?
state?需要從?props?初始化時使用 - 盡量不要使用,維護?
state/props?狀態(tài)一致性會增加復(fù)雜度 - 每次?
render?都會調(diào)用 - 典型場景: 表單控件獲取默認(rèn)值
render()::組件必須定義的一個生命周期方法,用來描述 虛擬 DOM 結(jié)構(gòu)
componentDidMount(): 用于數(shù)據(jù)請求,定義一些外部資源等等副作用
- UI 渲染完成后調(diào)用
- 只執(zhí)行一次
- 典型場景:獲取外部資源
4.2 更新階段
static getDerivedStateFromProps(nextProps, prevState)shouldComponentUpdate(nextProps, nextState):告訴組件是否需要重新渲染,用于性能優(yōu)化,比如判定指定 props 發(fā)生改變,組件才進行重新渲染
- 決定虛擬 DOM 是否需要重繪
- 一般可以由?
PureComponent?自動實現(xiàn) - 典型場景:性能優(yōu)化
render()getSnapshotBeforeUpdate(prevProps, prevState)- 在最近一次渲染輸出(提交到 DOM 節(jié)點)之前調(diào)用,state 已更新
- 與?
componentDidUpdate?搭配使用 - 典型場景:捕獲?
render?之前的 DOM 狀態(tài)
componentDidUpdate(prevProps, prevState)- 每次 UI 更新時被調(diào)用
- 典型場景:頁面需要根據(jù)?
props?變化重新獲取數(shù)據(jù)
4.3 卸載階段
componentWillUnmount(): 做些資源釋放,卸載副作用的事情
- 此方法中可以執(zhí)行必要的清理操作,例如,清除 timer,取消網(wǎng)絡(luò)請求或清除在
詳細(xì)內(nèi)容請參考React 知識體系之生命周期及使用場景[3]
五. 說說對 State 和 Props 的理解,有什么區(qū)別
state用于組件內(nèi)部數(shù)據(jù)傳遞,state?數(shù)據(jù)可以通過this.setSate或者useState進行修改props用于組件外部組件數(shù)據(jù)傳遞,props不能直接修改.主要使用場景是:- 兄弟組件通信
- 父子組件通信
"爺孫組件"組件通信
props的使用范圍雖然更加廣泛,但也有其局限性:對于嵌套層次較深的組件,如果使用props傳遞數(shù)據(jù),會導(dǎo)致代碼冗余,增加數(shù)據(jù)傳遞的復(fù)雜度
六. super 和 super(props)有什么區(qū)別
在 JavaScript 中,super 指的是父類構(gòu)造函數(shù)。(在我們的示例中,它指向 React.Component 實現(xiàn)。)
在調(diào)用父類的構(gòu)造函數(shù)之前,你是不能在?constructor?中使用?this?關(guān)鍵字的。JavaScript 不允許這個行為
class?Checkbox?extends?React.Component?{
??constructor(props)?{
????//?????還不能使用?`this`
????super(props);
????//????現(xiàn)在可以了
????this.state?=?{?isOn:?true?};
??}
??//?...
}
復(fù)制代碼
為什么一定要傳遞?props?呢?為了讓?React.Component?構(gòu)造函數(shù)能夠初始化?this.props
React 內(nèi)部代碼:
//?React?內(nèi)部
class?Component?{
??constructor(props)?{
????this.props?=?props;
????//?...
??}
}
復(fù)制代碼
實例代碼:
class?Button?extends?React.Component?{
??constructor(props)?{
????super();?//???We?forgot?to?pass?props
????console.log(props);?//???{}
????console.log(this.props);?//???undefined
??}
??//?...
}
復(fù)制代碼
class?Button?extends?React.Component?{
??constructor(props)?{
????super(props);?//???We?passed?props
????console.log(props);?//???{}
????console.log(this.props);?//???{}
??}
??//?...
}
復(fù)制代碼
有了 Hooks 以后,我們幾乎就不需要?super?和?this?了
詳細(xì)內(nèi)容請參考為什么我們要寫 super(props) ?[4]
七. 說說 React 中的 setState 機制
setState7.1 合成事件、鉤子函數(shù)中的 setState
- 在鉤子函數(shù)中?
setSate?拿不到最新值 - 在合成事件中執(zhí)行多個同樣的?
setSate,最終只會執(zhí)行一次,并且也拿不到最新值
原因:
- 一次?
setState?就會觸發(fā)一次?re-render(重渲染) - 為了避免頻繁的?
re-ernder,setState?被設(shè)計成異步的形式 - 每來一個?
setState,就把它塞進一個隊列里“攢起來”。等時機成熟,再把“攢起來”的?state?結(jié)果做合并(對于相同屬性的設(shè)置,React 只會為其保留最后一次的更新),最后只針對最新的?state?值走一次更新流程。這個過程,叫做**批量更新**
7.2?setTimeout/setInterval、原生 DOM中的 setState
- 在?
setTimeout/setInterval?中設(shè)置?setState,可以拿到最新的值 - 在
原生 DOM?事件中設(shè)置?setState,可以拿到最新的值
原因:
setState?的“異步”并不是說內(nèi)部由異步代碼實現(xiàn),其實源碼本身執(zhí)行的過程和代碼都是同步的,
只是合成事件和鉤子函數(shù)的調(diào)用順序在更新之前,導(dǎo)致在合成事件和鉤子函數(shù)中沒法立馬拿到更新后的值,形似了所謂的“異步”
setState?的批量更新優(yōu)化也是建立在“異步”(合成事件、鉤子函數(shù))之上的,在原生事件和?setTimeout?中不會批量更新
詳細(xì)內(nèi)容請參考setState 到底是同步的,還是異步的?[5]
八. 說說對 React 事件機制的理解
React事件機制8.1 React 中的事件是什么
React 中的事件叫合成事件:React 底層使用事件委托的方式對真實 DOM 事件進行了封裝,使合成事件具有更好的瀏覽器兼容性和性能
8.2 合成事件的大致原理
當(dāng)事件在具體的 DOM 節(jié)點上被觸發(fā)后,最終都會冒泡到 document 上,document 上所綁定的統(tǒng)一事件處理程序會將事件分發(fā)到具體的組件實例
8.3 React 為什么要重新設(shè)計出一個合成事件
合成事件是 React 自定義的事件對象,它符合 W3C 規(guī)范,在底層抹平了不同瀏覽器的差異,在上層面向開發(fā)者暴露統(tǒng)一的、穩(wěn)定的、與 DOM 原生事件相同的事件接口。
開發(fā)者們由此便不必再關(guān)注煩瑣的兼容性問題,可以專注于業(yè)務(wù)邏輯的開發(fā)。
- 雖然合成事件并不是原生 DOM 事件,但它保存了原生 DOM 事件的引用。當(dāng)你需要訪問原生 DOM 事件對象時,可以通過合成事件對象的?
e.nativeEvent?屬性獲取到它 - 合成事件無法獲取到真實 DOM,但可以通過 React 提供
refAPI 進行獲取
詳細(xì)內(nèi)容請參考React 事件與 DOM 事件有何不同?[6]
九. React 事件綁定的方式有哪些
9.1 類組件
9.1.1 render 方法中使用 bind
這種方式在組件每次 render 渲染的時候,都會重新進行 bind 的操作,影響性能
class?App?extends?React.Component?{
??handleClick()?{
????console.log("this?>?",?this);
??}
??render()?{
????return?<div?onClick={this.handleClick.bind(this)}>testdiv>;
??}
}
復(fù)制代碼
9.1.2 render 方法中使用箭頭函數(shù)
通過 ES6 的上下文來將 this 的指向綁定給當(dāng)前組件,同樣在每一次 render 的時候都會生成新的方法,影響性能
class?App?extends?React.Component?{
??handleClick()?{
????console.log("this?>?",?this);
??}
??render()?{
????return?<div?onClick={(e)?=>?this.handleClick(e)}>testdiv>;
??}
}
復(fù)制代碼
9.1.3 constructor 中 bind
在 constructor 中預(yù)先 bind 當(dāng)前組件,可以避免在 render 操作中重復(fù)綁定
class?App?extends?React.Component?{
??constructor(props)?{
????super(props);
????this.handleClick?=?this.handleClick.bind(this);
??}
??handleClick()?{
????console.log("this?>?",?this);
??}
??render()?{
????return?<div?onClick={this.handleClick}>testdiv>;
??}
}
復(fù)制代碼
9.1.4 定義階段使用箭頭函數(shù)綁定
能夠避免在 render 操作中重復(fù)綁定
class?App?extends?React.Component?{
??constructor(props)?{
????super(props);
??}
??handleClick?=?()?=>?{
????console.log("this?>?",?this);
??};
??render()?{
????return?<div?onClick={this.handleClick}>testdiv>;
??}
}
復(fù)制代碼
9.2 函數(shù)式組件
- 箭頭函數(shù)
函數(shù)組件沒有實例,因此沒有this
const?App?=?()?=>?{
??handleClick?=?(e)?=>?{
????console.log(e);
??};
??return?<div?onClick={this.handleClick}>testdiv>;
};
復(fù)制代碼
詳細(xì)內(nèi)容請參考React 構(gòu)建組件的方式有哪些[7]
十. React 構(gòu)建組件的方式有哪些
- 類組件
- 高階組件
render props- 純函數(shù)組件
Hooks組件- 自定義
Hooks
詳細(xì)內(nèi)容請參考React 構(gòu)建組件的方式有哪些[8]
十一. React 中組件通信的方式有哪些
- 單個組件內(nèi)部數(shù)據(jù)傳遞
state
- 父組件向子組件傳遞
props
- 子組件向父組件傳遞
props
- 兄弟組件之間的通信
props
- 父組件向后代組件傳遞
propsContext APIRedux發(fā)布-訂閱模式EventBus
- 非關(guān)系組件傳遞
Context APIRedux發(fā)布-訂閱模式EventBus
十二. React 的 key 有什么作用
說到 React 的 key,就要說到 React 的 Diff 算法
詳細(xì)內(nèi)容請參考React 列表循環(huán)為什么需要 key[9]
三、最后
關(guān)注我,一起攜手進階
歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進階~
點個在看支持我吧
參考資料
https://juejin.cn/post/6978685539985653767:?https://juejin.cn/post/6978685539985653767
[2]https://zh-hans.reactjs.org/docs/reconciliation.html#the-diffing-algorithm:?https://link.juejin.cn?target=https%3A%2F%2Fzh-hans.reactjs.org%2Fdocs%2Freconciliation.html%23the-diffing-algorithm
[3]https://juejin.cn/post/6981739846461030408:?https://juejin.cn/post/6981739846461030408
[4]https://overreacted.io/zh-hans/why-do-we-write-super-props/:?https://link.juejin.cn?target=https%3A%2F%2Foverreacted.io%2Fzh-hans%2Fwhy-do-we-write-super-props%2F
[5]https://www.yuque.com/u221766/xgl0mb/oxl3ik:?https://link.juejin.cn?target=https%3A%2F%2Fwww.yuque.com%2Fu221766%2Fxgl0mb%2Foxl3ik
[6]https://www.yuque.com/u221766/xgl0mb/iu68y0:?https://link.juejin.cn?target=https%3A%2F%2Fwww.yuque.com%2Fu221766%2Fxgl0mb%2Fiu68y0
[7]https://juejin.cn/post/6952907248393781284#heading-2:?https://juejin.cn/post/6952907248393781284#heading-2
[8]https://juejin.cn/post/6952907248393781284:?https://juejin.cn/post/6952907248393781284
[9]https://juejin.cn/post/6940974776441634823:?https://juejin.cn/post/6940974776441634823
來源:望道同學(xué)
https://juejin.cn/post/6981831831112908831
