前段時(shí)間準(zhǔn)備面試塌西,總結(jié)了很多,下面是我在準(zhǔn)備React面試時(shí)筝尾,結(jié)合自己的實(shí)際面試經(jīng)歷,以及我以前源碼分析的文章站辉,總結(jié)出來的一些React高頻面試題目损姜。
以前我寫的源碼分析的文章摧阅,并沒有很多人看,因?yàn)榇蟛糠智闆r下你不需要深入源碼也能懂得其中原理棒卷,并解決實(shí)際問題比规,這也是我總結(jié)這些面試題的原因苞俘,讓你在更短的時(shí)間內(nèi)獲得更大的收益。
由于是以面試題的角度來討論乞封,所以某些點(diǎn)可能不能非常深入岗憋,我在問題下面都貼了相關(guān)鏈接仔戈,如果想深入理解监徘,請(qǐng)點(diǎn)擊這些文章。
由于題目較多墓卦,分為上户敬、下兩篇,本篇文章我們先來討論如下19個(gè)題目:
React生命周期有哪些呢堰,16版本生命周期發(fā)生了哪些變化凡泣?
setState是同步的還是異步的鞋拟?
為什么有時(shí)連續(xù)多次setState只有一次生效?
React如何實(shí)現(xiàn)自己的事件機(jī)制?
為何React事件要自己綁定this哮笆?
原生事件和React事件的區(qū)別汰扭?
React的合成事件是什么萝毛?
React和原生事件的執(zhí)行順序是什么?可以混用嗎环揽?
虛擬Dom是什么歉胶?
虛擬Dom比普通Dom更快嗎巴粪?
虛擬Dom中的$$typeof屬性的作用是什么肛根?
React組件的渲染流程是什么?
為什么代碼中一定要引入React臼氨?
為什么React組件首字母必須大寫一也?
React在渲染真實(shí)Dom時(shí)做了哪些性能優(yōu)化?
什么是高階組件抑月?如何實(shí)現(xiàn)谦絮?
HOC在業(yè)務(wù)場景中有哪些實(shí)際應(yīng)用場景层皱?
高階組件(HOC)和Mixin的異同點(diǎn)是什么赠潦?
Hook有哪些優(yōu)勢她奥?
React生命周期有哪些,16版本生命周期發(fā)生了哪些變化绷跑?
15生命周期
初始化階段
constructor構(gòu)造函數(shù)
getDefaultPropsprops默認(rèn)值
getInitialStatestate默認(rèn)值
掛載階段
componentWillMount組件初始化渲染前調(diào)用
render組件渲染
componentDidMount組件掛載到DOM后調(diào)用
更新階段
componentWillReceiveProps組件將要接收新props前調(diào)用
shouldComponentUpdate組件是否需要更新
componentWillUpdate組件更新前調(diào)用
render組件渲染
componentDidUpdate組件更新后調(diào)用
卸載階段
componentWillUnmount組件卸載前調(diào)用
16生命周期
初始化階段
constructor構(gòu)造函數(shù)
getDefaultPropsprops默認(rèn)值
getInitialStatestate默認(rèn)值
掛載階段
staticgetDerivedStateFromProps(props,state)
render
componentDidMount
getDerivedStateFromProps:組件每次被rerender的時(shí)候,包括在組件構(gòu)建之后(虛擬dom之后垦藏,實(shí)際dom掛載之前)鸳谜,每次獲取新的props或state之后咐扭;每次接收新的props之后都會(huì)返回一個(gè)對(duì)象作為新的state蝗肪,返回null則說明不需要更新state;配合componentDidUpdate辛馆,可以覆蓋componentWillReceiveProps的所有用法
更新階段
staticgetDerivedStateFromProps(props,state)
shouldComponentUpdate
render
getSnapshotBeforeUpdate(prevProps,prevState)
componentDidUpdate
getSnapshotBeforeUpdate:觸發(fā)時(shí)間:update發(fā)生的時(shí)候昙篙,在render之后,在組件dom渲染之前缴挖;返回一個(gè)值映屋,作為componentDidUpdate的第三個(gè)參數(shù)同蜻;配合componentDidUpdate, 可以覆蓋componentWillUpdate的所有用法
卸載階段
componentWillUnmount
錯(cuò)誤處理
componentDidCatch
React16新的生命周期棄用了componentWillMount湾蔓、componentWillReceivePorps默责,componentWillUpdate新增了getDerivedStateFromProps、getSnapshotBeforeUpdate來代替棄用的三個(gè)鉤子函數(shù)。
React16并沒有刪除這三個(gè)鉤子函數(shù)葡缰,但是不能和新增的鉤子函數(shù)混用泛释,React17將會(huì)刪除這三個(gè)鉤子函數(shù)温算,新增了對(duì)錯(cuò)誤的處理(componentDidCatch)
setState是同步的還是異步的注竿?
生命周期和合成事件中
在React的生命周期和合成事件中巩割,React仍然處于他的更新機(jī)制中,這時(shí)無論調(diào)用多少次setState愈犹,都會(huì)不會(huì)立即執(zhí)行更新漩怎,而是將要更新的·存入_pendingStateQueue,將要更新的組件存入dirtyComponent饭玲。
當(dāng)上一次更新機(jī)制執(zhí)行完畢咱枉,以生命周期為例蚕断,所有組件入挣,即最頂層組件didmount后會(huì)將批處理標(biāo)志設(shè)置為false径筏。這時(shí)將取出dirtyComponent中的組件以及_pendingStateQueue中的state進(jìn)行更新滋恬。這樣就可以確保組件不會(huì)被重新渲染多次。
? componentDidMount() {
? ? this.setState({
? ? ? index: this.state.index + 1
? ? })
? ? console.log('state', this.state.index);
? }
所以带斑,如上面的代碼勋磕,當(dāng)我們?cè)趫?zhí)行setState后立即去獲取state敢靡,這時(shí)是獲取不到更新后的state的啸胧,因?yàn)樘幱赗eact的批處理機(jī)制中纺念,state被暫存起來柠辞,待批處理機(jī)制完成之后,統(tǒng)一進(jìn)行更新习勤。
所以图毕。setState本身并不是異步的,而是React的批處理機(jī)制給人一種異步的假象囤官。
異步代碼和原生事件中
? componentDidMount() {
? ? setTimeout(() => {
? ? ? console.log('調(diào)用setState');
? ? ? this.setState({
? ? ? ? index: this.state.index + 1
? ? ? })
? ? ? console.log('state', this.state.index);
? ? }, 0);
? }
如上面的代碼党饮,當(dāng)我們?cè)诋惒酱a中調(diào)用setState時(shí)刑顺,根據(jù)JavaScript的異步機(jī)制饲常,會(huì)將異步代碼先暫存贝淤,等所有同步代碼執(zhí)行完畢后在執(zhí)行播聪,這時(shí)React的批處理機(jī)制已經(jīng)走完犬耻,處理標(biāo)志設(shè)被設(shè)置為false执泰,這時(shí)再調(diào)用setState即可立即執(zhí)行更新术吝,拿到更新后的結(jié)果排苍。
在原生事件中調(diào)用setState并不會(huì)出發(fā)React的批處理機(jī)制,所以立即能拿到最新結(jié)果传藏。
最佳實(shí)踐
setState的第二個(gè)參數(shù)接收一個(gè)函數(shù)毯侦,該函數(shù)會(huì)在React的批處理機(jī)制完成之后調(diào)用侈离,所以你想在調(diào)用setState后立即獲取更新后的值卦碾,請(qǐng)?jiān)谠摶卣{(diào)函數(shù)中獲取。
? this.setState({ index: this.state.index + 1 }, () => {
? ? ? console.log(this.state.index);
? ? })
推薦閱讀:由實(shí)際問題探究setState的執(zhí)行機(jī)制
為什么有時(shí)連續(xù)多次setState只有一次生效济榨?
例如下面的代碼腿短,兩次打印出的結(jié)果是相同的:
? componentDidMount() {
? ? this.setState({ index: this.state.index + 1 }, () => {
? ? ? console.log(this.state.index);
? ? })
? ? this.setState({ index: this.state.index + 1 }, () => {
? ? ? console.log(this.state.index);
? ? })
? }
原因就是React會(huì)批處理機(jī)制中存儲(chǔ)的多個(gè)setState進(jìn)行合并橘忱,來看下React源碼中的_assign函數(shù)钝诚,類似于Object的assign:
_assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
如果傳入的是對(duì)象凝颇,很明顯會(huì)被合并成一次疹鳄,所以上面的代碼兩次打印的結(jié)果是相同的:
Object.assign(
? nextState,
? {index: state.index+ 1},
? {index: state.index+ 1}
)
注意瘪弓,assign函數(shù)中對(duì)函數(shù)做了特殊處理腺怯,處理第一個(gè)參數(shù)傳入的是函數(shù)呛占,函數(shù)的參數(shù)preState是前一次合并后的結(jié)果肴茄,所以計(jì)算結(jié)果是準(zhǔn)確的:
? componentDidMount() {
? ? this.setState((state, props) => ({
? ? ? ? index: state.index + 1
? ? }), () => {
? ? ? console.log(this.state.index);
? ? })
? ? this.setState((state, props) => ({
? ? ? ? index: state.index + 1
? ? }), () => {
? ? ? console.log(this.state.index);
? ? })
? }
所以上面的代碼兩次打印的結(jié)果是不同的仅叫。
最佳實(shí)踐
React會(huì)對(duì)多次連續(xù)的setState進(jìn)行合并惑芭,如果你想立即使用上次setState后的結(jié)果進(jìn)行下一次setState遂跟,可以讓setState接收一個(gè)函數(shù)而不是一個(gè)對(duì)象幻锁。這個(gè)函數(shù)用上一個(gè)state作為第一個(gè)參數(shù)边臼,將此次更新被應(yīng)用時(shí)的props做為第二個(gè)參數(shù)柠并。
React如何實(shí)現(xiàn)自己的事件機(jī)制臼予?
React事件并沒有綁定在真實(shí)的Dom節(jié)點(diǎn)上粘拾,而是通過事件代理缰雇,在最外層的document上對(duì)事件進(jìn)行統(tǒng)一分發(fā)。
組件掛載疏之、更新時(shí):
通過lastProps锋爪、nextProps判斷是否新增、刪除事件分別調(diào)用事件注冊(cè)沃呢、卸載方法薄霜。
調(diào)用EventPluginHub的enqueuePutListener進(jìn)行事件存儲(chǔ)
獲取document對(duì)象惰瓜。
根據(jù)事件名稱(如onClick、onCaptureClick)判斷是進(jìn)行冒泡還是捕獲备禀。
判斷是否存在addEventListener方法曲尸,否則使用attachEvent(兼容IE)另患。
給document注冊(cè)原生事件回調(diào)為dispatchEvent(統(tǒng)一的事件分發(fā)機(jī)制)昆箕。
事件初始化:
EventPluginHub負(fù)責(zé)管理React合成事件的callback鹏倘,它將callback存儲(chǔ)在listenerBank中肺稀,另外還存儲(chǔ)了負(fù)責(zé)合成事件的Plugin话原。
獲取綁定事件的元素的唯一標(biāo)識(shí)key繁仁。
將callback根據(jù)事件類型黄虱,元素的唯一標(biāo)識(shí)key存儲(chǔ)在listenerBank中捻浦。
listenerBank的結(jié)構(gòu)是:listenerBank[registrationName][key]朱灿。
觸發(fā)事件時(shí):
觸發(fā)document注冊(cè)原生事件的回調(diào)dispatchEvent
獲取到觸發(fā)這個(gè)事件最深一級(jí)的元素
遍歷這個(gè)元素的所有父元素盗扒,依次對(duì)每一級(jí)元素進(jìn)行處理。
構(gòu)造合成事件缕碎。
將每一級(jí)的合成事件存儲(chǔ)在eventQueue事件隊(duì)列中池户。
遍歷eventQueue煞檩。
通過isPropagationStopped判斷當(dāng)前事件是否執(zhí)行了阻止冒泡方法斟湃。
如果阻止了冒泡凝赛,停止遍歷墓猎,否則通過executeDispatch執(zhí)行合成事件毙沾。
釋放處理完成的事件左胞。
React在自己的合成事件中重寫了stopPropagation方法烤宙,將isPropagationStopped設(shè)置為true躺枕,然后在遍歷每一級(jí)事件的過程中根據(jù)此遍歷判斷是否繼續(xù)執(zhí)行拐云。這就是React自己實(shí)現(xiàn)的冒泡機(jī)制。
為何React事件要自己綁定this膳帕?
在上面提到的事件處理流程中备闲,React在document上進(jìn)行統(tǒng)一的事件分發(fā)恬砂,dispatchEvent通過循環(huán)調(diào)用所有層級(jí)的事件來模擬事件冒泡和捕獲泻骤。
在React源碼中狱掂,當(dāng)具體到某一事件處理函數(shù)將要調(diào)用時(shí)趋惨,將調(diào)用invokeGuardedCallback方法器虾。
function invokeGuardedCallback(name, func, a) {
? try {
? ? func(a);
? } catch (x) {
? ? if (caughtError === null) {
? ? ? caughtError = x;
? ? }
? }
}
可見兆沙,事件處理函數(shù)是直接調(diào)用的葛圃,并沒有指定調(diào)用的組件库正,所以不進(jìn)行手動(dòng)綁定的情況下直接獲取到的this是不準(zhǔn)確的诀诊,所以我們需要手動(dòng)將當(dāng)前組件綁定到this上属瓣。
原生事件和React事件的區(qū)別抡蛙?
React事件使用駝峰命名,而不是全部小寫魂迄。
通過JSX, 你傳遞一個(gè)函數(shù)作為事件處理程序粗截,而不是一個(gè)字符串。
在React中你不能通過返回false來阻止默認(rèn)行為捣炬。必須明確調(diào)用preventDefault熊昌。
React的合成事件是什么绽榛?
React根據(jù)W3C規(guī)范定義了每個(gè)事件處理函數(shù)的參數(shù),即合成事件婿屹。
事件處理程序?qū)鬟fSyntheticEvent的實(shí)例,這是一個(gè)跨瀏覽器原生事件包裝器昂利。它具有與瀏覽器原生事件相同的接口届腐,包括stopPropagation()和preventDefault(),在所有瀏覽器中他們工作方式都相同蜂奸。
React合成的SyntheticEvent采用了事件池犁苏,這樣做可以大大節(jié)省內(nèi)存,而不會(huì)頻繁的創(chuàng)建和銷毀事件對(duì)象扩所。
另外围详,不管在什么瀏覽器環(huán)境下,瀏覽器會(huì)將該事件類型統(tǒng)一創(chuàng)建為合成事件祖屏,從而達(dá)到了瀏覽器兼容的目的短曾。
React和原生事件的執(zhí)行順序是什么?可以混用嗎赐劣?
React的所有事件都通過document進(jìn)行統(tǒng)一分發(fā)嫉拐。當(dāng)真實(shí)Dom觸發(fā)事件后冒泡到document后才會(huì)對(duì)React事件進(jìn)行處理。
所以原生的事件會(huì)先執(zhí)行魁兼,然后執(zhí)行React合成事件婉徘,最后執(zhí)行真正在document上掛載的事件
React事件和原生事件最好不要混用。原生事件中如果執(zhí)行了stopPropagation方法咐汞,則會(huì)導(dǎo)致其他React事件失效盖呼。因?yàn)樗性氐氖录o法冒泡到document上,導(dǎo)致所有的React事件都將無法被觸發(fā)化撕。几晤。
虛擬Dom是什么?
在原生的JavaScript程序中植阴,我們直接對(duì)DOM進(jìn)行創(chuàng)建和更改蟹瘾,而DOM元素通過我們監(jiān)聽的事件和我們的應(yīng)用程序進(jìn)行通訊。
而React會(huì)先將你的代碼轉(zhuǎn)換成一個(gè)JavaScript對(duì)象掠手,然后這個(gè)JavaScript對(duì)象再轉(zhuǎn)換成真實(shí)DOM憾朴。這個(gè)JavaScript對(duì)象就是所謂的虛擬DOM。
當(dāng)我們需要?jiǎng)?chuàng)建或更新元素時(shí)喷鸽,React首先會(huì)讓這個(gè)VitrualDom對(duì)象進(jìn)行創(chuàng)建和更改众雷,然后再將VitrualDom對(duì)象渲染成真實(shí)DOM。
當(dāng)我們需要對(duì)DOM進(jìn)行事件監(jiān)聽時(shí),首先對(duì)VitrualDom進(jìn)行事件監(jiān)聽砾省,VitrualDom會(huì)代理原生的DOM事件從而做出響應(yīng)鸡岗。
推薦閱讀:【React深入】深入分析虛擬DOM的渲染過程和特性
虛擬Dom比普通Dom更快嗎?
很多文章說VitrualDom可以提升性能编兄,這一說法實(shí)際上是很片面的纤房。
直接操作DOM是非常耗費(fèi)性能的,這一點(diǎn)毋庸置疑翻诉。但是React使用VitrualDom也是無法避免操作DOM的。
如果是首次渲染捌刮,VitrualDom不具有任何優(yōu)勢碰煌,甚至它要進(jìn)行更多的計(jì)算,消耗更多的內(nèi)存绅作。
VitrualDom的優(yōu)勢在于React的Diff算法和批處理策略芦圾,React在頁面更新之前,提前計(jì)算好了如何進(jìn)行更新和渲染DOM俄认。實(shí)際上个少,這個(gè)計(jì)算過程我們?cè)谥苯硬僮鱀OM時(shí),也是可以自己判斷和實(shí)現(xiàn)的眯杏,但是一定會(huì)耗費(fèi)非常多的精力和時(shí)間夜焦,而且往往我們自己做的是不如React好的。所以岂贩,在這個(gè)過程中React幫助我們"提升了性能"茫经。
所以,我更傾向于說萎津,VitrualDom幫助我們提高了開發(fā)效率卸伞,在重復(fù)渲染時(shí)它幫助我們計(jì)算如何更高效的更新,而不是它比DOM操作更快锉屈。
虛擬Dom中的$$typeof屬性的作用是什么荤傲?
ReactElement中有一個(gè)$$typeof屬性,它被賦值為REACT_ELEMENT_TYPE:
var REACT_ELEMENT_TYPE =
? (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
? 0xeac7;
可見颈渊,$$typeof是一個(gè)Symbol類型的變量遂黍,這個(gè)變量可以防止XSS。
如果你的服務(wù)器有一個(gè)漏洞俊嗽,允許用戶存儲(chǔ)任意JSON對(duì)象妓湘, 而客戶端代碼需要一個(gè)字符串,這可能會(huì)成為一個(gè)問題:
// JSON
let expectedTextButGotJSON = {
? type: 'div',
? props: {
? ? dangerouslySetInnerHTML: {
? ? ? __html: '/* put your exploit here */'
? ? },
? },
};
let message = { text: expectedTextButGotJSON };
<p>
? {message.text}
</p>
JSON中不能存儲(chǔ)Symbol類型的變量乌询。
ReactElement.isValidElement函數(shù)用來判斷一個(gè)React組件是否是有效的榜贴,下面是它的具體實(shí)現(xiàn)。
ReactElement.isValidElement = function (object) {
? return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
};
可見React渲染時(shí)會(huì)把沒有$$typeof標(biāo)識(shí),以及規(guī)則校驗(yàn)不通過的組件過濾掉唬党。
當(dāng)你的環(huán)境不支持Symbol時(shí)鹃共,$$typeof被賦值為0xeac7,至于為什么驶拱,React開發(fā)者給出了答案:
0xeac7看起來有點(diǎn)像React霜浴。
React組件的渲染流程是什么?
使用React.createElement或JSX編寫React組件蓝纲,實(shí)際上所有的JSX代碼最后都會(huì)轉(zhuǎn)換成React.createElement(...)阴孟,Babel幫助我們完成了這個(gè)轉(zhuǎn)換的過程。
createElement函數(shù)對(duì)key和ref等特殊的props進(jìn)行處理税迷,并獲取defaultProps對(duì)默認(rèn)props進(jìn)行賦值永丝,并且對(duì)傳入的孩子節(jié)點(diǎn)進(jìn)行處理,最終構(gòu)造成一個(gè)ReactElement對(duì)象(所謂的虛擬DOM)箭养。
ReactDOM.render將生成好的虛擬DOM渲染到指定容器上慕嚷,其中采用了批處理、事務(wù)等機(jī)制并且對(duì)特定瀏覽器進(jìn)行了性能優(yōu)化毕泌,最終轉(zhuǎn)換為真實(shí)DOM喝检。
為什么代碼中一定要引入React?
JSX只是為React.createElement(component,props,...children)方法提供的語法糖撼泛。
所有的JSX代碼最后都會(huì)轉(zhuǎn)換成React.createElement(...)挠说,Babel幫助我們完成了這個(gè)轉(zhuǎn)換的過程。
所以使用了JSX的代碼都必須引入React愿题。
為什么React組件首字母必須大寫纺涤?
babel在編譯時(shí)會(huì)判斷JSX中組件的首字母,當(dāng)首字母為小寫時(shí)抠忘,其被認(rèn)定為原生DOM標(biāo)簽撩炊,createElement的第一個(gè)變量被編譯為字符串;當(dāng)首字母為大寫時(shí)崎脉,其被認(rèn)定為自定義組件拧咳,createElement的第一個(gè)變量被編譯為對(duì)象;
React在渲染真實(shí)Dom時(shí)做了哪些性能優(yōu)化囚灼?
在IE(8-11)和Edge瀏覽器中骆膝,一個(gè)一個(gè)插入無子孫的節(jié)點(diǎn),效率要遠(yuǎn)高于插入一整個(gè)序列化完整的節(jié)點(diǎn)樹灶体。
React通過lazyTree阅签,在IE(8-11)和Edge中進(jìn)行單個(gè)節(jié)點(diǎn)依次渲染節(jié)點(diǎn),而在其他瀏覽器中則首先將整個(gè)大的DOM結(jié)構(gòu)構(gòu)建好蝎抽,然后再整體插入容器政钟。
并且,在單獨(dú)渲染節(jié)點(diǎn)時(shí),React還考慮了fragment等特殊節(jié)點(diǎn)养交,這些節(jié)點(diǎn)則不會(huì)一個(gè)一個(gè)插入渲染精算。
什么是高階組件?如何實(shí)現(xiàn)碎连?
高階組件可以看作React對(duì)裝飾模式的一種實(shí)現(xiàn)灰羽,高階組件就是一個(gè)函數(shù),且該函數(shù)接受一個(gè)組件作為參數(shù)鱼辙,并返回一個(gè)新的組件廉嚼。
高階組件(HOC)是React中的高級(jí)技術(shù),用來重用組件邏輯倒戏。但高階組件本身并不是ReactAPI怠噪。它只是一種模式,這種模式是由React自身的組合性質(zhì)必然產(chǎn)生的峭梳。
function visible(WrappedComponent) {
? return class extends Component {
? ? render() {
? ? ? const { visible, ...props } = this.props;
? ? ? if (visible === false) return null;
? ? ? return <WrappedComponent {...props} />;
? ? }
? }
}
上面的代碼就是一個(gè)HOC的簡單應(yīng)用,函數(shù)接收一個(gè)組件作為參數(shù)蹂喻,并返回一個(gè)新組件葱椭,新組建可以接收一個(gè)visible props,根據(jù)visible的值來判斷是否渲染Visible口四。
我們可以通過以下兩種方式實(shí)現(xiàn)高階組件:
屬性代理
函數(shù)返回一個(gè)我們自己定義的組件孵运,然后在render中返回要包裹的組件,這樣我們就可以代理所有傳入的props蔓彩,并且決定如何渲染治笨,實(shí)際上 ,這種方式生成的高階組件就是原組件的父組件赤嚼,上面的函數(shù)visible就是一個(gè)HOC屬性代理的實(shí)現(xiàn)方式旷赖。
function proxyHOC(WrappedComponent) {
? return class extends Component {
? ? render() {
? ? ? return <WrappedComponent {...this.props} />;
? ? }
? }
}
對(duì)比原生組件增強(qiáng)的項(xiàng):
可操作所有傳入的props
可操作組件的生命周期
可操作組件的static方法
獲取refs
反向繼承
返回一個(gè)組件,繼承原組件更卒,在render中調(diào)用原組件的render等孵。由于繼承了原組件,能通過this訪問到原組件的生命周期蹂空、props俯萌、state、render等上枕,相比屬性代理它能操作更多的屬性咐熙。
function inheritHOC(WrappedComponent) {
? return class extends WrappedComponent {
? ? render() {
? ? ? return super.render();
? ? }
? }
}
對(duì)比原生組件增強(qiáng)的項(xiàng):
可操作所有傳入的props
可操作組件的生命周期
可操作組件的static方法
獲取refs
可操作state
可以渲染劫持
推薦閱讀:【React深入】從Mixin到HOC再到Hook
HOC在業(yè)務(wù)場景中有哪些實(shí)際應(yīng)用場景?
HOC可以實(shí)現(xiàn)的功能:
組合渲染
條件渲染
操作props
獲取refs
狀態(tài)管理
操作state
渲染劫持
HOC在業(yè)務(wù)中的實(shí)際應(yīng)用場景:
日志打點(diǎn)
權(quán)限控制
雙向綁定
表單校驗(yàn)
具體實(shí)現(xiàn)請(qǐng)參考我這篇文章:https://juejin.im/post/5cad39b3f265da03502b1c0a
高階組件(HOC)和Mixin的異同點(diǎn)是什么辨萍?
Mixin和HOC都可以用來解決React的代碼復(fù)用問題棋恼。
圖片來源于網(wǎng)絡(luò)
Mixin可能會(huì)相互依賴,相互耦合,不利于代碼維護(hù)
不同的Mixin中的方法可能會(huì)相互沖突
Mixin非常多時(shí)蘸泻,組件是可以感知到的琉苇,甚至還要為其做相關(guān)處理,這樣會(huì)給代碼造成滾雪球式的復(fù)雜性
而HOC的出現(xiàn)可以解決這些問題:
高階組件就是一個(gè)沒有副作用的純函數(shù)悦施,各個(gè)高階組件不會(huì)互相依賴耦合
高階組件也有可能造成沖突并扇,但我們可以在遵守約定的情況下避免這些行為
高階組件并不關(guān)心數(shù)據(jù)使用的方式和原因,而被包裹的組件也不關(guān)心數(shù)據(jù)來自何處抡诞。高階組件的增加不會(huì)為原組件增加負(fù)擔(dān)
Hook有哪些優(yōu)勢穷蛹?
減少狀態(tài)邏輯復(fù)用的風(fēng)險(xiǎn)
Hook和Mixin在用法上有一定的相似之處,但是Mixin引入的邏輯和狀態(tài)是可以相互覆蓋的昼汗,而多個(gè)Hook之間互不影響肴熏,這讓我們不需要在把一部分精力放在防止避免邏輯復(fù)用的沖突上。在不遵守約定的情況下使用HOC也有可能帶來一定沖突顷窒,比如props覆蓋等等蛙吏,使用Hook則可以避免這些問題。
避免地獄式嵌套
大量使用HOC的情況下讓我們的代碼變得嵌套層級(jí)非常深鞋吉,使用HOC鸦做,我們可以實(shí)現(xiàn)扁平式的狀態(tài)邏輯復(fù)用,而避免了大量的組件嵌套谓着。
讓組件更容易理解
在使用class組件構(gòu)建我們的程序時(shí)泼诱,他們各自擁有自己的狀態(tài),業(yè)務(wù)邏輯的復(fù)雜使這些組件變得越來越龐大赊锚,各個(gè)生命周期中會(huì)調(diào)用越來越多的邏輯治筒,越來越難以維護(hù)。使用Hook舷蒲,可以讓你更大限度的將公用邏輯抽離耸袜,將一個(gè)組件分割成更小的函數(shù),而不是強(qiáng)制基于生命周期方法進(jìn)行分割牲平。
使用函數(shù)代替class
相比函數(shù)句灌,編寫一個(gè)class可能需要掌握更多的知識(shí),需要注意的點(diǎn)也越多欠拾,比如this指向胰锌、綁定事件等等。另外藐窄,計(jì)算機(jī)理解一個(gè)class比理解一個(gè)函數(shù)更快资昧。Hooks讓你可以在classes之外使用更多React的新特性。