React
1.1 虛擬dom和真實(shí)dom
什么是虛擬dom既穆?
虛擬 dom 相當(dāng)于在 js 和真實(shí) dom 中間加了一個(gè)緩存楷拳,利用 dom diff 算法避免了沒有必要的 dom操作暮芭,從而 提高性能猪贪。
- 用 JavaScript 對(duì)象結(jié)構(gòu)表示 DOM 樹的結(jié)構(gòu)
- 用這個(gè)樹構(gòu)建一個(gè)真正的 DOM 樹诸迟,插到文檔當(dāng)中當(dāng)狀態(tài)變更的時(shí)候塔鳍,重新構(gòu)造一棵新的對(duì)象樹壹粟。
- 用新的樹和舊的樹進(jìn)行比較拜隧,記錄兩棵樹差異把 2 所記錄的差異應(yīng)用到步驟 1 所構(gòu)建的真正的DOM 樹上,視圖就更新了趁仙。
虛擬dom和real dom區(qū)別 性能差異
減少DOM的操作:虛擬dom可以將多次操作合并為一次操作洪添,減少DOM操作的次數(shù)
Real DOM | Virtual DOM |
---|---|
更新緩慢 | 更新更快 |
可以直接更新 HTML | 無法直接更新 HTML |
如果元素更新,則創(chuàng)建新DOM | 如果元素更新雀费,則更新 JSX |
DOM操作代價(jià)很高 | DOM 操作非常簡單 |
消耗的內(nèi)存較多 | 很少的內(nèi)存消耗 |
1.2 react組件間通信
父組件向子組件通訊: 父組件可以向子組件通過傳 props 的方式干奢,向子組件進(jìn)行通訊
子組件向父組件通訊: props+回調(diào)的方式,父組件向子組件傳遞props進(jìn)行通訊,此props為作用域?yàn)楦附M件自身的函數(shù)盏袄,子組件調(diào)用該函數(shù)忿峻,將子組件想要傳遞的信息,作為參數(shù)辕羽,傳遞到父組件的作用域中
兄弟組件通信: 找到這兩個(gè)兄弟節(jié)點(diǎn)共同的父節(jié)點(diǎn),結(jié)合上面兩種方式由父節(jié)點(diǎn)轉(zhuǎn)發(fā)信息進(jìn)行通信
跨層級(jí)通信: Context 設(shè)計(jì)目的是為了共享那些對(duì)于一個(gè)組件樹而言是“全局”的數(shù)據(jù)逛尚,例如當(dāng)前認(rèn)證的用戶、主題或首選語言
發(fā)布訂閱模式: 發(fā)布者發(fā)布事件刁愿,訂閱者監(jiān)聽事件并做出反應(yīng),我們可以通過引入event模塊進(jìn)行通信
全局狀態(tài)管理工具: 借助Redux或者M(jìn)obx等全局狀態(tài)管理工具進(jìn)行通信,這種工具會(huì)維護(hù)一個(gè)全局狀態(tài)中心Store,并根據(jù)不同的事件產(chǎn)生新的狀態(tài)
1.3 redux的原理
Redux:Redux 是當(dāng)今最熱門的前端開發(fā)庫之一绰寞。它是 JavaScript 程序的可預(yù)測狀態(tài)容器,用于整個(gè)應(yīng)用的狀態(tài)管理铣口。使用 Redux 開發(fā)的應(yīng)用易于測試滤钱,可以在不同環(huán)境中運(yùn)行,并顯示一致的行為
數(shù)據(jù)流
首先脑题,用戶(通過View)發(fā)出Action件缸,發(fā)出方式就用到了dispatch方法
然后,Store自動(dòng)調(diào)用Reducer叔遂,并且傳入兩個(gè)參數(shù):當(dāng)前State和收到的Action他炊,Reducer會(huì)返回新的State
State一旦有變化,Store就會(huì)調(diào)用監(jiān)聽函數(shù)掏熬,來更新View
Redux遵循的三個(gè)原則是什么
單一事實(shí)來源:整個(gè)應(yīng)用的狀態(tài)存儲(chǔ)在單個(gè) store 中的對(duì)象/狀態(tài)樹里佑稠。單一狀態(tài)樹可以更容易地跟蹤隨時(shí)間的變化,并調(diào)試或檢查應(yīng)用程序旗芬。
狀態(tài)是只讀的:改變狀態(tài)的唯一方法是去觸發(fā)一個(gè)動(dòng)作舌胶。動(dòng)作是描述變化的普通 JS 對(duì)象。就像state 是數(shù)據(jù)的最小表示一樣疮丛,該操作是對(duì)數(shù)據(jù)更改的最小表示幔嫂。
使用純函數(shù)進(jìn)行更改:為了指定狀態(tài)樹如何通過操作進(jìn)行轉(zhuǎn)換辆它,你需要純函數(shù)。純函數(shù)是那些返回值僅取決于其參數(shù)值的函數(shù)履恩。
單一事實(shí)來源怎么理解锰茉?
Redux 使用 “Store” 將程序的整個(gè)狀態(tài)存儲(chǔ)在同一個(gè)地方。因此所有組件的狀態(tài)都存儲(chǔ)在 Store 中切心,并且它們從 Store 本身接收更新飒筑。單一狀態(tài)樹可以更容易地跟蹤隨時(shí)間的變化,并調(diào)試或檢查程序绽昏。
組件組成
- Action – 這是一個(gè)用來描述發(fā)生了什么事情的對(duì)象
- Reducer – 這是一個(gè)確定狀態(tài)將如何變化的地方
- Store – 整個(gè)程序的狀態(tài)/對(duì)象樹保存在Store中
- View – 只顯示 Store 提供的數(shù)據(jù)
如何在 Redux 中定義 Action协屡?
React 中的 Action 必須具有 type 屬性,該屬性指示正在執(zhí)行的 ACTION 的類型全谤。必須將它們定義為字符串常量肤晓,并且還可以向其添加更多的屬性。在 Redux 中认然,action 被名為 Action Creators 的函數(shù)所創(chuàng)建
解釋 Reducer 的作用
Reducers 是純函數(shù)补憾,它規(guī)定應(yīng)用程序的狀態(tài)怎樣因響應(yīng) ACTION 而改變。Reducers 通過接受先前的狀態(tài)和 action 來工作卷员,然后它返回一個(gè)新的狀態(tài)盈匾。它根據(jù)操作的類型確定需要執(zhí)行哪種更新,然后返回新的值子刮。如果不需要完成任務(wù)威酒,它會(huì)返回原來的狀態(tài)。
Store 在 Redux 中的意義是什么挺峡?
Store 是一個(gè) JavaScript 對(duì)象,它可以保存程序的狀態(tài)担钮,并提供一些方法來訪問狀態(tài)橱赠、調(diào)度操作和注冊(cè)偵聽器。應(yīng)用程序的整個(gè)狀態(tài)/對(duì)象樹保存在單一存儲(chǔ)中箫津。因此狭姨,Redux 非常簡單且是可預(yù)測的。我們可以將中間件傳遞到 store 來處理數(shù)據(jù)苏遥,并記錄改變存儲(chǔ)狀態(tài)的各種操作饼拍。所有操作都通過 reducer 返回一個(gè)新狀態(tài)。
Redux 有哪些優(yōu)點(diǎn)田炭?
- 結(jié)果的可預(yù)測性 - 由于總是存在一個(gè)真實(shí)來源师抄,即 store ,因此不存在如何將當(dāng)前狀態(tài)與動(dòng)作和應(yīng)用的其他部分同步的問題
- 可維護(hù)性 - 代碼變得更容易維護(hù)教硫,具有可預(yù)測的結(jié)果和嚴(yán)格的結(jié)構(gòu)
- 服務(wù)器端渲染 - 你只需將服務(wù)器上創(chuàng)建的 store 傳到客戶端即可叨吮。這對(duì)初始渲染非常有用辆布,并且可以優(yōu)化應(yīng)用性能,從而提供更好的用戶體驗(yàn)
- 開發(fā)人員工具 - 從操作到狀態(tài)更改茶鉴,開發(fā)人員可以實(shí)時(shí)跟蹤應(yīng)用中發(fā)生的所有事情
- 社區(qū)和生態(tài)系統(tǒng) - Redux 背后有一個(gè)巨大的社區(qū)锋玲,這使得它更加迷人。一個(gè)由才華橫溢的人組成的大型社區(qū)為庫的改進(jìn)做出了貢獻(xiàn)涵叮,并開發(fā)了各種應(yīng)用
- 易于測試 - Redux 的代碼主要是小巧惭蹂、純粹和獨(dú)立的功能。這使代碼可測試且獨(dú)立
- 組織 - Redux 準(zhǔn)確地說明了代碼的組織方式割粮,這使得代碼在團(tuán)隊(duì)使用時(shí)更加一致和簡單
1.4 React組件生命周期的階段是什么剿干?
初始渲染階段:這是組件即將開始其生命之旅并進(jìn)入 DOM 的階段。
更新階段:一旦組件被添加到 DOM穆刻,它只有在 prop 或狀態(tài)發(fā)生變化時(shí)才可能更新和重新渲染置尔。這些只發(fā)生在這個(gè)階段。
卸載階段:這是組件生命周期的最后階段氢伟,組件被銷毀并從 DOM 中刪除榜轿。
1.5 詳細(xì)解釋React 組件的生命周期方法
掛載階段:
constructor: 構(gòu)造函數(shù),最先被執(zhí)行,我們通常在構(gòu)造函數(shù)里初始化state對(duì)象或者給自定義方法綁定this
getDerivedStateFromProps: static getDerivedStateFromProps(nextProps, prevState) ,這是個(gè)靜態(tài)方法,當(dāng)我們接收到新的屬性想去修改我們state朵锣,可以使用getDerivedStateFromProps
render: render函數(shù)是純函數(shù)谬盐,只返回需要渲染的東西,不應(yīng)該包含其它的業(yè)務(wù)邏輯,可以返回原生的DOM诚些、React組件飞傀、Fragment、Portals诬烹、字符串和數(shù)字砸烦、Boolean和null等內(nèi)容
componentDidMount: 組件裝載之后調(diào)用,此時(shí)我們可以獲取到DOM節(jié)點(diǎn)并操作绞吁,比如對(duì)canvas幢痘,svg的操作,服務(wù)器請(qǐng)求家破,訂閱都可以寫在這個(gè)里面颜说,但是記得在componentWillUnmount中取消訂閱
更新階段:
- getDerivedStateFromProps: 此方法在更新個(gè)掛載階段都可能會(huì)調(diào)用
- shouldComponentUpdate: shouldComponentUpdate(nextProps, nextState) ,有兩個(gè)參數(shù)nextProps和nextState,表示新的屬性和變化之后的state汰聋,返回一個(gè)布爾值门粪,true表示會(huì)觸發(fā)重新渲染,false表示不會(huì)觸發(fā)重新渲染烹困,默認(rèn)返回true,我們通常利用此生命周期來優(yōu)化React程序性能
- render: 更新階段也會(huì)觸發(fā)此生命周期
- getSnapshotBeforeUpdate: getSnapshotBeforeUpdate(prevProps, prevState) ,這個(gè)方法在render之后玄妈,componentDidUpdate之前調(diào)用,有兩個(gè)參數(shù)prevProps和prevState,表示之前的屬性和之前的state措近,這個(gè)函數(shù)有一個(gè)返回值溶弟,會(huì)作為第三個(gè)參數(shù)傳給componentDidUpdate,如果你不想要返回值瞭郑,可以返回null辜御,此生命周期必須與componentDidUpdate搭配使用
- componentDidUpdate: componentDidUpdate(prevProps, prevState, snapshot) ,該方法在getSnapshotBeforeUpdate方法之后被調(diào)用,有三個(gè)參數(shù)prevProps屈张,prevState擒权,snapshot,表示之前的props阁谆,之前的state碳抄,和snapshot。第三個(gè)參數(shù)是getSnapshotBeforeUpdate返回的,如果觸發(fā)某些回調(diào)函數(shù)時(shí)需要用到 DOM 元素的狀態(tài)场绿,則將對(duì)比或計(jì)算的過程遷移至getSnapshotBeforeUpdate剖效,然后在 componentDidUpdate 中統(tǒng)一觸發(fā)回調(diào)或更新狀態(tài)
卸載階段:
- componentWillUnmount: 當(dāng)我們的組件被卸載或者銷毀了就會(huì)調(diào)用,我們可以在這個(gè)函數(shù)里去清除一些定時(shí)器焰盗,取消網(wǎng)絡(luò)請(qǐng)求璧尸,清理無效的DOM元素等垃圾清理工作
擴(kuò)展:
React 16之后有三個(gè)生命周期被廢棄(但并未刪除)
componentWillMount
componentWillReceiveProps
componentWillUpdate
官方計(jì)劃在17版本完全刪除這三個(gè)函數(shù),只保留UNSAVE_前綴的三個(gè)函數(shù)熬拒,目的是為了向下兼容爷光,但是對(duì)于開發(fā)者而言應(yīng)該盡量避免使用他們,而是使用新增的生命周期函數(shù)替代它們
1.6 router
- 什么是React 路由澎粟?
React 路由是一個(gè)構(gòu)建在 React 之上的強(qiáng)大的路由庫蛀序,它有助于向應(yīng)用程序添加新的屏幕和流。這使 URL 與網(wǎng)頁上顯示的數(shù)據(jù)保持同步活烙。它負(fù)責(zé)維護(hù)標(biāo)準(zhǔn)化的結(jié)構(gòu)和行為徐裸,并用于開發(fā)單頁 Web 應(yīng)用。 React 路由有一個(gè)簡單的API瓣颅。
- 為什么需要 React 中的路由倦逐?
Router 用于定義多個(gè)路由,當(dāng)用戶定義特定的 URL 時(shí)宫补,如果此 URL 與 Router 內(nèi)定義的任何 “路
由” 的路徑匹配,則用戶將重定向到該特定路由曾我。所以基本上我們需要在自己的應(yīng)用中添加一個(gè)
Router 庫粉怕,允許創(chuàng)建多個(gè)路由,每個(gè)路由都會(huì)向我們提供一個(gè)獨(dú)特的視圖
- 為什么React Router v4中使用 switch 關(guān)鍵字 抒巢?
雖然 <div> 用于封裝 Router 中的多個(gè)路由贫贝,當(dāng)你想要僅顯示要在多個(gè)定義的路線中呈現(xiàn)的單個(gè)路
線時(shí),可以使用 “switch” 關(guān)鍵字。使用時(shí)稚晚, <switch> 標(biāo)記會(huì)按順序?qū)⒁讯x的 URL 與已定義的
路由進(jìn)行匹配崇堵。找到第一個(gè)匹配項(xiàng)后,它將渲染指定的路徑客燕。從而繞過其它路線鸳劳。
-
列出 React Router 的優(yōu)點(diǎn)
4.1 就像 React 基于組件一樣,在 React Router v4 中也搓,API 是 'All About Components'赏廓。可以將Router 可視化為單個(gè)根組件()傍妒,其中我們將特定的子路由()包起來幔摸。
4.2 無需手動(dòng)設(shè)置歷史值:在 React Router v4 中,我們要做的就是將路由包裝在 組件中颤练。
4.3 包是分開的:共有三個(gè)包既忆,分別用于 Web、Native 和 Core嗦玖。這使我們應(yīng)用更加緊湊患雇。基于類似的編碼風(fēng)格很容易進(jìn)行切換踏揣。
1.7 React 的 refs 有什么了解庆亡?
Refs 是 React 中引用的簡寫。它是一個(gè)有助于存儲(chǔ)對(duì)特定的 React 元素或組件的引用的屬性捞稿,它將由組件渲染配置函數(shù)返回又谋。用于對(duì) render() 返回的特定元素或組件的引用。當(dāng)需要進(jìn)行 DOM 測量或向組件添加方法時(shí)娱局,它們會(huì)派上用場彰亥。
class ReferenceDemo extends React.Component{
display() {
const name = this.inputDemo.value;
document.getElementById('disp').innerHTML = name;
}
render() {
return(
<div>
Name: <input type="text" ref={input => this.inputDemo = input} />
<button name="Click" onClick={this.display}>Click</button>
<h2>Hello <span id="disp"></span> !!!</h2>
</div>
);
}
}
1.8 列出一些應(yīng)該使用 Refs 的情況
React并不是將click事件綁在該div的真實(shí)DOM上,而是在document處監(jiān)聽所有支持的事件衰齐,當(dāng)事件發(fā)生并冒泡至document處時(shí)任斋,React將事件內(nèi)容封裝并交由真正的處理函數(shù)運(yùn)行。這樣的方式不僅減少了內(nèi)存消耗耻涛,還能在組件掛載銷毀時(shí)統(tǒng)一訂閱和移除事件废酷。
另外冒泡到 document 上的事件也不是原生瀏覽器事件,而是 React 自己實(shí)現(xiàn)的合成事件(SyntheticEvent)抹缕。因此我們?nèi)绻幌胍录芭莸脑挸后。{(diào)用 event.stopPropagation 是無效的,而應(yīng)該調(diào)用event.preventDefault
1.9 redux-saga和mobx的比較
- 狀態(tài)管理
redux-sage 是 redux 的一個(gè)異步處理的中間件卓研。
mobx 是數(shù)據(jù)管理庫趴俘,和 redux 一樣睹簇。
- 設(shè)計(jì)思想
redux-sage 屬于 flux 體系, 函數(shù)式編程思想寥闪。
mobx 不屬于 flux 體系太惠,面向?qū)ο缶幊毯晚憫?yīng)式編程。
- 主要特點(diǎn)
redux-sage 因?yàn)槭侵虚g件疲憋,更關(guān)注異步處理的凿渊,通過 Generator 函數(shù)來將異步變?yōu)橥剑勾a可讀性高柜某,結(jié)構(gòu)清晰嗽元。action 也不是 action creator 而是 pure action,在 Generator 函數(shù)中通過 call 或者 put 方法直接聲明式調(diào)用喂击,并自帶一些方法剂癌,如 takeEvery,takeLast翰绊,race等佩谷,控制多個(gè)異步操作,讓多個(gè)異步更簡單监嗜。
mobx 是更簡單更方便更靈活的處理數(shù)據(jù)谐檀。 Store 是包含了 state 和 action。state 包裝成一個(gè)可被觀察的對(duì)象裁奇, action 可以直接修改 state桐猬,之后通過 Computed values 將依賴 state 的計(jì)算屬性更新 ,之后觸發(fā) Reactions 響應(yīng)依賴 state 的變更刽肠,輸出相應(yīng)的副作用 溃肪,但不生成新的 state。
- 數(shù)據(jù)可變性
redux-sage 強(qiáng)調(diào) state 不可變音五,不能直接操作 state惫撰,通過 action 和 reducer 在原來的 state 的基礎(chǔ)上返回一個(gè)新的 state 達(dá)到改變 state 的目的。
mobx 直接在方法中更改 state躺涝,同時(shí)所有使用的 state 都發(fā)生變化厨钻,不生成新的 state。
- 寫法難易度
redux-sage 比 redux 在 action 和 reducer 上要簡單一些坚嗜。需要用 dispatch 觸發(fā) state 的改變夯膀,需要 mapStateToProps 訂閱 state。
mobx 在非嚴(yán)格模式下不用 action 和 reducer苍蔬,在嚴(yán)格模式下需要在 action 中修改 state棍郎,并且自動(dòng)觸發(fā)相關(guān)依賴的更新。
- 使用場景
redux-sage 很好的解決了 redux 關(guān)于異步處理時(shí)的復(fù)雜度和代碼冗余的問題银室,數(shù)據(jù)流向比較好追蹤。但是 redux 的學(xué)習(xí)成本比 較高,代碼比較冗余蜈敢,不是特別需要狀態(tài)管理辜荠,最好用別的方式代替。
mobx 學(xué)習(xí)成本低抓狭,能快速上手伯病,代碼比較簡潔。但是可能因?yàn)榇a編寫的原因和數(shù)據(jù)更新時(shí)相對(duì)黑盒否过,導(dǎo)致數(shù)據(jù)流向不利于追蹤午笛。
1.10 簡述一下 React 的源碼實(shí)現(xiàn)
React 的實(shí)現(xiàn)主要分為 Component 和 Element ;
Component 屬于 React 實(shí)例苗桂,在創(chuàng)建實(shí)例的過程中會(huì)在實(shí)例中注冊(cè) state 和 props 屬性药磺,還會(huì)依次調(diào)用內(nèi)置的生命周期函數(shù);
Component 中有一個(gè) render 函數(shù)煤伟, render 函數(shù)要求返回一個(gè) Element 對(duì)象(或 null )癌佩;
Element 對(duì)象分為原生 Element 對(duì)象和組件式對(duì)象,原生 Element + 組件式對(duì)象會(huì)被一起解析成虛擬 DOM 樹便锨,并且內(nèi)部使用的 state 和 props 也以 AST 的形式注入到這棵虛擬 DOM 樹之中围辙;
在渲染虛擬 DOM 樹的前后,會(huì)觸發(fā) React Component 的一些生命周期鉤子函數(shù)放案,比如componentWillMount 和 componentDidMount 姚建,在虛擬 DOM 樹解析完成后將被渲染成真實(shí)DOM 樹;
調(diào)用 setState 時(shí)吱殉,會(huì)調(diào)用更新函數(shù)更新 Component 的 state 掸冤,并且觸發(fā)內(nèi)部的一個(gè)updater ,調(diào)用 render 生成新的虛擬 DOM 樹考婴,利用 diff 算法與舊的虛擬 DOM 樹進(jìn)行比對(duì)贩虾,比對(duì)以后利用最優(yōu)的方案進(jìn)行 DOM 節(jié)點(diǎn)的更新,這也是 React 單向數(shù)據(jù)流的原理(與 Vue 的MVVM 不同之處)沥阱。