1、 keys 的作用是什么?
Keys 是 React 用于追蹤哪些列表中元素被修改、被添加或者被移除的輔助標(biāo)識(shí)。
render () {
return (
<ul>
{this.state.todoItems.map(({item}) => {
return <li key={item.id}>{item.name}</li>
})}
</ul>
)
}
在開發(fā)過程中,我們需要保證某個(gè)元素的 key 在其同級(jí)元素中具有唯一性。在 React Diff 算法中 React 會(huì)借助元素的 Key 值來(lái)判斷該元素是新近創(chuàng)建的還是被移動(dòng)而來(lái)的元素,從而減少不必要的元素重渲染。
盡量不要使用元素在列表中的索引值作為key,因?yàn)榱斜碇械脑仨樞蛞坏┌l(fā)生改變,就可能導(dǎo)致大量的key失效,進(jìn)而引起大量的修改操作,最好用item項(xiàng)的id作為key值。
2、setState相關(guān)
React中constructor是唯一可以初始化state的地方,也可以把它理解成一個(gè)鉤子函數(shù),該函數(shù)最先執(zhí)行且只執(zhí)行一次。即直接通過this.state = {}來(lái)對(duì)state進(jìn)行初始化,在其他位置需要通過this.setState函數(shù)來(lái)更新狀態(tài)。直接修改this.state雖然狀態(tài)可以改變,但不會(huì)觸發(fā)組件的更新。
this.setState(),該方法接收兩種參數(shù):對(duì)象或函數(shù)。
對(duì)象:即想要修改的state
函數(shù):接收兩個(gè)函數(shù),第一個(gè)函數(shù)接受兩個(gè)參數(shù),第一個(gè)是當(dāng)前state,第二個(gè)是當(dāng)前props,該函數(shù)返回一個(gè)對(duì)象,和直接傳遞對(duì)象參數(shù)是一樣的,就是要修改的state;第二個(gè)函數(shù)參數(shù)是state改變后觸發(fā)的回調(diào)。
官方有這樣一段描述:setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall.
從這段描述可以看出,setState函數(shù)可能是異步的。先了解下setState執(zhí)行后會(huì)發(fā)生什么:
在代碼中調(diào)用 setState 函數(shù)之后,React 會(huì)將傳入的參數(shù)對(duì)象與組件當(dāng)前的狀態(tài)合并,然后觸發(fā)所謂的調(diào)和過程(Reconciliation)。經(jīng)過調(diào)和過程,React 會(huì)以相對(duì)高效的方式根據(jù)新的狀態(tài)構(gòu)建 React 元素樹并且著手重新渲染整個(gè) UI 界面。在 React 得到元素樹之后,React 會(huì)自動(dòng)計(jì)算出新的樹與老樹的節(jié)點(diǎn)差異,然后根據(jù)差異對(duì)界面進(jìn)行最小化重渲染。在差異計(jì)算算法中,React 能夠相對(duì)精確地知道哪些位置發(fā)生了改變以及應(yīng)該如何改變,這就保證了按需更新,而不是全部重新渲染。
假如setState是同步更新的,每更新一次,這個(gè)過程都要完整執(zhí)行一次,無(wú)疑會(huì)造成性能問題。為了批次和效能,多個(gè)setState有可能在執(zhí)行過程中還會(huì)被合并,所以setState延時(shí)異步更新是很合理的。
下面看看setState是何時(shí)同步何時(shí)異步的?由React控制的事件處理程序,以及生命周期函數(shù)調(diào)用setState不會(huì)同步更新state 。大部分開發(fā)中用到的都是React封裝的事件,比如onChange、onClick、onTouchMove等,這些事件處理程序中的setState都是異步處理的。React控制之外的事件中調(diào)用setState是同步更新的。比如原生js綁定的事件,setTimeout/setInterval等。
在 React 的 setState 函數(shù)實(shí)現(xiàn)中,會(huì)根據(jù)一個(gè)變量 isBatchingUpdates 判斷是直接更新 this.state 還是放到隊(duì)列中延時(shí)更新,而 isBatchingUpdates 默認(rèn)是 false,表示 setState 會(huì)同步更新 this.state;但是,有一個(gè)函數(shù) batchedUpdates,該函數(shù)會(huì)把 isBatchingUpdates 修改為 true,而當(dāng) React 在調(diào)用事件處理函數(shù)之前就會(huì)先調(diào)用這個(gè) batchedUpdates將isBatchingUpdates修改為true,這樣由 React 控制的事件處理過程 setState 不會(huì)同步更新 this.state。
3、子組件的數(shù)據(jù)依賴于父組件componentWillReceiveProps
如果子組件的數(shù)據(jù)依賴于父組件,將會(huì)執(zhí)行一個(gè)鉤子函數(shù)componentWillReceiveProps,在生命周期的第一次render后不會(huì)被調(diào)用,但是會(huì)在之后的每次render中被調(diào)用 = 當(dāng)父組件再次傳送props
componentWillReceiveProps(nextProps){
if(this.props.activeKey !== nextProps.activeKey){
this.doSomething();
}
}
4、shouldComponentUpdate 是做什么的,(react 性能優(yōu)化是哪個(gè)周期函數(shù)?)
shouldComponentUpdate 這個(gè)方法用來(lái)判斷是否需要調(diào)用 render 方法重新描繪 dom。因?yàn)?dom 的描繪非常消耗性能,如果我們能在 shouldComponentUpdate 方法中能夠?qū)懗龈鼉?yōu)化的 dom diff 算法,可以極大的提高性能。
5、為什么說(shuō)虛擬dom會(huì)提高性能?
說(shuō)Virtual DOM高效的一個(gè)理由就是它不會(huì)直接操作原生的DOM節(jié)點(diǎn),因?yàn)檫@個(gè)很消耗性能。當(dāng)組件狀態(tài)變化時(shí)它會(huì)通過某些diff算法去計(jì)算出本次數(shù)據(jù)更新真實(shí)的視圖變化,然后只改變“需要改變”的DOM節(jié)點(diǎn)。
虛擬 dom 相當(dāng)于在 js 和真實(shí) dom 中間加了一個(gè)緩存,利用 dom diff 算法避免了沒有必要的 dom 操作,從而提高性能。
用過React的人可能都會(huì)體會(huì)到React并沒有想象中那么高效,框架有時(shí)候會(huì)做很多無(wú)用功,這體現(xiàn)在很多組件會(huì)被“無(wú)緣無(wú)故”進(jìn)行重渲染(re-render)。注意這里說(shuō)的re-render和對(duì)原生DOM進(jìn)行操作是兩碼事!所謂的re-render是你定義的class Component的render方法被重新執(zhí)行,或者你的組件函數(shù)被重新執(zhí)行。組件被重渲染是因?yàn)閂itual DOM的高效是建立在diff算法上的,而要有diff一定要將組件重渲染才能知道組件的新狀態(tài)和舊狀態(tài)有沒有發(fā)生改變,從而才能計(jì)算出哪些DOM需要被更新。
6、react組件什么時(shí)候會(huì)重渲染?
只有在組件的state變化時(shí)才會(huì)出發(fā)組件的重新渲染。狀態(tài)的改變可以因?yàn)?code>props的改變,或者直接通過setState方法改變。組件獲得新的狀態(tài)然后React決定是否應(yīng)該重新渲染組件。不幸的是,React難以置信簡(jiǎn)單地將默認(rèn)行為設(shè)計(jì)為每次都重新渲染。
比如,有一個(gè)state每?jī)擅胝{(diào)用一個(gè)setState,甚至這個(gè)state在整個(gè)組件中完全沒有使用到,但是組件每次都會(huì)進(jìn)行重渲染,這就造成了極大的性能浪費(fèi)。當(dāng)然,這是為了安全考慮,如果狀態(tài)改變但是組件沒有正確渲染的話更糟。
于是,為了避免這種情況,我們可以告訴React跳過重新渲染,即使用shouldComponentUpdate函數(shù)。
shouldComponentUpdate方法默認(rèn)返回true,這就是導(dǎo)致每次更新都重新渲染的原因。但是你可以在需要優(yōu)化性能時(shí)重寫這個(gè)方法來(lái)讓React更智能。比起讓React每次都重新渲染,你可以告訴React你什么時(shí)候不觸發(fā)重新渲染。
7、react diff 原理
- 把樹形結(jié)構(gòu)按照層級(jí)分解,只比較同級(jí)元素。
- 給列表結(jié)構(gòu)的每個(gè)單元添加唯一的 key 屬性,方便比較。
- React 只會(huì)匹配相同 class 的 component(這里面的 class 指的是組件的名字)
- 合并操作,調(diào)用 component 的 setState 方法的時(shí)候, React 將其標(biāo)記為 dirty.到每一個(gè)事件循環(huán)結(jié)束, React 檢查所有標(biāo)記 dirty 的 component 重新繪制.
- 選擇性子樹渲染。開發(fā)人員可以重寫 shouldComponentUpdate 提高 diff 的性能。
8、無(wú)狀態(tài)組件與純組件
無(wú)狀態(tài)組件可以通過減少繼承Component而來(lái)的生命周期函數(shù)而達(dá)到性能優(yōu)化的效果。從本質(zhì)上來(lái)說(shuō),無(wú)狀態(tài)組件就是一個(gè)單純的render函數(shù),所以無(wú)狀態(tài)組件的缺點(diǎn)也是顯而易見的。因?yàn)樗鼪]有shouldComponentUpdate生命周期函數(shù),所以每次state更新,它都會(huì)重新繪制render函數(shù)。
純組件是通過控制shouldComponentUpdate生命周期函數(shù),減少render調(diào)用次數(shù)來(lái)減少性能損耗的。這相對(duì)于Component來(lái)說(shuō),減少了手動(dòng)判斷state變化的繁瑣操作,但該組件也具有一定的缺陷,因?yàn)樗荒苓M(jìn)行一層淺比較,簡(jiǎn)單來(lái)說(shuō),它只比較props和state的內(nèi)存地址,如果內(nèi)存地址相同,則shouldComponentUpdate生命周期就返回false。PureComponent的使用場(chǎng)景應(yīng)該是局部數(shù)據(jù)發(fā)生改變的場(chǎng)景,比如帶有輸入框、switch開關(guān)等的UI組件就可以使用PureComponent組件封裝。PureComponent中如果有數(shù)據(jù)操作最好配合一個(gè)第三方組件——Immutable一起使用,Immutable需要使用npm安裝該插件才可以使用,因?yàn)镮mmutable可以保證數(shù)據(jù)的不變性。
9、類組件(Class component)和函數(shù)式組件(Functional component)之間有何不同
- 類組件不僅允許你使用更多額外的功能,如組件自身的狀態(tài)和生命周期鉤子,也能使組件直接訪問 store 并維持狀態(tài)
- 當(dāng)組件僅是接收 props,并將組件自身渲染到頁(yè)面時(shí),該組件就是一個(gè) '無(wú)狀態(tài)組件(stateless component)',可以使用一個(gè)純函數(shù)來(lái)創(chuàng)建這樣的組件。這種組件也被稱為啞組件(dumb components)或展示組件
10、React hooks使用
基礎(chǔ)hook useState和useReducer提供了可以刷新(更新)函數(shù)組件的途徑。同樣,也是自定義hook刷新的途徑。如果想讓自定義hook去刷新函數(shù)組件,那只能在自定義組件中使用useState或者useReducer來(lái)強(qiáng)制刷新,達(dá)到類似以前forUpdate的效果。
State hook的主要作用就是獲取需要的 state 和 更新state的方法
const [state, setState] = useState(initialState);
import React, { useState } from 'react'
export default function () {
const [count, setCount] = useState(0)
return <div>
<button onClick={() => {
setCount(count + 1)
}}>+</button>
{count}
<button onClick={() => {
setCount(count - 1)
}}>-</button>
</div>
}
Effect hook方法是在每次渲染之后執(zhí)行,可以理解為class寫法中的 componentDidMount / componentDidUpdate(為了方便理解可以這么理解,但不完全一樣)
useEffect(didUpdate);
11、狀態(tài)(state)和屬性(props)之間有何不同
- State 是一種數(shù)據(jù)結(jié)構(gòu),用于組件掛載時(shí)所需數(shù)據(jù)的默認(rèn)值。State 可能會(huì)隨著時(shí)間的推移而發(fā)生突變,但多數(shù)時(shí)候是作為用戶事件行為的結(jié)果。
- Props(properties 的簡(jiǎn)寫)則是組件的配置。props 由父組件傳遞給子組件,并且就子組件而言,props 是不可變的(immutable)。組件不能改變自身的 props,但是可以把其子組件的 props 放在一起(統(tǒng)一管理)。Props 也不僅僅是數(shù)據(jù)--回調(diào)函數(shù)也可以通過 props 傳遞。