一、react 面試題
redux
中間件的原理的理解,答案如下所示:
redux
是一個應用數據流框架,主要是解決了組件間狀態共享的問題,原理是集中式管理,主要有三個核心方法,action
,store
,reducer
- 工作流程是
view
調用store
的dispatch
接收action
傳入store
,reducer
進行state
操作,view
通過store
提供的getState
獲取最新的數據 - 新增
state
,對狀態的管理更加明確,通過redux
,流程更加規範了,減少手動編碼量,提高了編碼效率,同時缺點時當數據更新時有時候組件不需要,但是也要重新繪製,有些影響效率。一般情況下,我們在構建多交互,多數據流的複雜項目應用時纔會使用它們 - 常用的一些
redux
中間件,如下所示:redux-thunk
:處理異步操作redux-saga
:處理異步操作redux-promise
:處理異步操作,actionCreator
的返回值是promise
- 中間件的執行原理和
koa
中間件的執行原理類似,但是不是洋蔥型的,而是半個洋蔥,因爲redux
是單向執行的。當你應用了中間件,在觸發一個action
操作的時候,action
操作就會經過先經過中間件,最終再形成dispatch(action)
。一個觸發一個action
動作的時候,代碼的執行邏輯。thunk
是允許dispatch
一個函數,而不是一個對象 thunk
中間件的內部,代碼如下所示:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
// 如果是函數,就執行函數
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// 如果不是,執行下一個中間件
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
- 中間件的內部邏輯,代碼如下所示:
const store = createStore(reducer, preloadedState, enchancer);
// 如果沒有中間件,正常觸發一個action;
// 如果有中間件的時候 creatStore內部的執行邏輯是這樣的
// enchancer 就是你應用的中間件,調用applyMiddleware得到的組合中間件
function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// 該創建的store還是要創建的,只傳入了兩個參數,沒有中間件,得到的是正常的store
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
// 把getState、dispatch傳給中間件
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 下面方法返回來了一個函數數組,中間件被剝離到
// next => {}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 再執行下面的,中間件就被剝離到
// action => {}
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
// 下面得到的結果是
// 假設中間件爲 a b
// a(b(store.dispatch))
return enchancer(createStore)(reducer, preloadedState);
// 結合上面thunk的源碼
({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
};
- 經過上面的操作後,經過中間件包裝後的
store
的樣子,假設中間件爲a b c
,每一箇中間件配發了一個原始store
的dispatch
,中間件函數嵌套執行,如下所示- 沒有中間件,代碼如下
store = { dispatch, ... ..., subscribe, getState }
- 經過中間包裝後,代碼如下
store = { dispatch: a(b((store.dispatch))), ... ..., subscribe, getState }
- 沒有中間件,代碼如下
redux-thunk
就是一個封裝函數,允許store.dispatch
一個函數,compsoe
內部,如下所示:
// compose本身並不改變函數的執行,將函數組合後又返回了一個函數
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
- 你會把數據統一放到
redux
中管理,還是共享數據放在redux
中管理,答案如下所示:
-
不是
react native
,所有數據都要放在redux
中管理。如果只是把共用的數據放在redux
中,一個組件中會既有state、props和redux
存儲數據,那麼當頁面出現問題,要從state、props和redux
三個方面檢查,開發程序是很快的,但是最費時間的是程序後期的可維護性和代碼的可調節性。如果數據都放在redux
中管理,項目出錯以後,就只用檢查redux
,定位錯位很快。不要想着state
中的數據只會供一個組件使用,在項目越來越大的時候,說不準別的組件會需要使用,redux
中可以存儲5GB
的數據。所以,能用redux
的時候一定要用redux
,對於後期的維護來說很方便 -
immutable
,當你把redux
和immutable
這個庫結合使用的時候,你整個項目的性能會達到最優,而且非常非常簡單。沒有數據臃腫的顧慮,你不存在redux
中,你也需要存儲在state
或者props
中。
componentWillReceiveProps
的調用時機,答案如下所示:
- 父組件中改變了
props
傳值時觸發的函數 props
改變的時候纔會調用,父組件第一次往子組件傳值的時候,不會調用
react
性能優化的最佳實踐,答案如下所示:
PureComponent
,自帶shouldcomponentupdate
,是一個淺比較,代碼如下所示:
class Test extends React.PureComponents {
constructor(props) {
super(props)
}
render() {
return <div>hello</div>
}
}
- 通過與
immutable.js
庫的結合,完美的解決react
的性能問題 - 優化的核心是減少不必要的渲染
- 增加
shouldComponentUpdate
鉤子對新舊props
和state
進行比較,如果值相同則阻止更新,避免不必要的渲染,或者使用PureReactComponent
替代Component
,其內部已經封裝了shouldComponentUpdate
的淺比較邏輯 - 對於列表或其他結構相同的節點,爲其中的每一項增加唯一
key
屬性,以方便React
的diff
算法中對該節點的複用,減少節點的創建和刪除操作 render
函數中減少類似onClick={() => {doSomething()}}
的寫法,每次調用render
函數時均會創建一個新的函數,即使內容沒有發生任何變化,也會導致節點沒必要的重渲染,建議將函數保存在組件的成員對象中,這樣只會創建一次webpack-bundle-analyzer
分析當前頁面的依賴包,是否存在不合理性
- 虛擬
dom
的理解,虛擬dom
會提升代碼性能的原因,答案如下所示:
- 虛擬
dom
就是真實dom
的一個js
對象 - 以前需要兩個頁面的差異,需要去比對真實
dom
的比對,真實的dom
節點會有事件,屬性,還會有各種各樣的方法。所以兩個真實dom
的比對會非常耗性能。於是把dom
對象變成js
對象,js
對象就沒有dom
對象上亂七八糟的特性了,js
對象就比較快。
webpack
中,藉助loader
完成的JSX
代碼的轉化,還是babel
,答案如下所示:
babel - preset-react
所去完成的轉化
- 調用
setState
後,發生的過程,答案如下所示:
- 調用
setState
函數之後,react
會將傳入的參數對象與組件當前的狀態合併,然後觸發調和過程(Reconciliation)
,以高效方式根據新的狀態構建React
元素樹並且着手重新渲染整個UI界面 - 在
React
得到元素樹之後,React
會自動計算出新的樹與老樹的節點差異,然後根據差異對界面進行最小化重渲染,按需渲染,不是全部渲染 - 調用函數,通過一個函數返回一個對象,代碼如下所示:
this.setState({
age: this.state.age + 1
}) // 如果是連續點擊一個按鈕調用這個setState,會出現數值不是一個一個加上去的,而是會出現一次幾個的變化,因爲react會把多個setState整合爲一個,最後在改變state。
this.setState((prevState) => ({
age: ++ prevState.age
})) // 不管你怎麼瘋狂的點擊按鈕,都會一個一個往上加。
setState
是異步的,這個點你在什麼時候遇到過坑,答案如下所示:
- 使用
setState
的時候返回傳一個函數,同上所示 - 對於
setState
是異步還是同步主要看是誰在調用它,大部分情況下是異步的,小部分情況是同步的 - 異步的情況,如下所示:
- 在
React
代理的合成事件中調用,如onClick、onChange
事件,這些事件都是React
爲了代理原生事件而封裝出來的一整套事件機制,我們稱之爲合成事件 - 在鉤子函數(生命週期)中調用
setState
的異步不是真的異步,setState
本身的執行過程是同步的,是React
將多個setState
進行合併和批量更新,導致其看起來像是異步的
- 在
- 同步的情況,如下所示:
-
在原生事件中調用,代碼如下:
class App extends React.Component{ ... componentDidMount(){ document.querySelector('#A').addEventListener('click',()=>{ this.setState({ // 這裏的 setState 是同步的 }) }) } }
-
在setTimeout()中調用
-
refs
的作用是什麼,你在什麼業務場景下使用過refs
,答案如下所示:
refs
的作用是操作dom
,訪問DOM
元素或者某個組件實例- 在業務場景下使用過
refs
,展示一個圖片,獲取圖片的寬高,這也是因爲react
不能直接操作dom
- 放大鏡,獲取圖片寬高等等
- 當頁面滾動,監聽頁面滾動的事件,代碼如下所示:
class Test extends Component {
// 需求:當頁面滾動,監聽頁面滾動的事件
constructor(props) {
super(props);
this.state = {
top: 0
}
this.handleWindowScroll = this.handleWindowScroll.bind(this)
}
handleWindowScroll() {
this.setState(() => ({
top: document.body.scrollTop
}))
}
componentDidMount() {
window.addEventListener('scroll', this.handleWindowScroll)
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleWindowScroll)
}
render() {
return <div>{this.state.top}</div>
}
}
ref
是一個函數,好處是什麼,答案如下所示:
- 方便
react
在銷燬或者重新渲染組件的時候去有效的去清空ref
裏面的東西,防止內存泄漏,以後ref
不要用字符串的形式了,要用函數式的寫法 - 代碼如下所示:
class Test extends Component {
componentDidMount() {
this.elem
}
render() {
return <div ref={(div) => { this.elem = div }}></div> // ref使用的時候最好使用函數
}
}
- 高階組件的理解,它的本質是什麼,答案如下所示:
- 在
react
裏面不要去使用繼承,爲什麼,設計模式中有這樣一句話“組合優於繼承”,react
這種組件式的編程,是一種組合類型的設計模式,一定是優於繼承的,可維護性是比繼承高的多,react
中所有問題都是可以利用組件拼合的方法解決的。 - 高階組件實際上就是一個函數,接收參數,返回參數。對一個組件進行包裝,然後再返回一個組件。爲什麼對一個組件進行包裝呢,因爲組件有可能很多地方要用,這個組件的大部分東西在別的地方都可以直接用,只有少數的地方有區別,我們就可以把共用的地方寫到高階組件裏面去,而通過往高階組件裏面傳遞參數,來去動態這個組件在使用時的差異。
- 高階組件地獄,新版本的
hook
解決了這個問題,代碼如下所示:
<A>
<B>
<C>
<D />
</C>
</B>
</A>
- 高階組件其實就是一個函數而已,只不過參數是一個組件而已,返回了一個新的組件。複用組件的業務邏輯
react-redux connect
其實就是一個高階組件。HOC
是純函數,沒有副作用。純函數是輸入確定,輸出就一定確定
- 受控組件和非受控組件的區別,答案如下所示:
- 在
HTML
中,類似<input/>, <select> 和 <textarea>
這樣的表單元素會維護自身的狀態,並基於用戶的輸入來更新。當用戶提交表單時,前面提到的元素的值將隨表單一起被髮送。但在React
中會有些不同,包含表單元素的組件將會在state
中追蹤輸入的值,並且每次調用回調函數時,如onChange
會更新state
,重新渲染組件。一個輸入表單元素,它的值通過React
的這種方式來控制,這樣的元素就被稱爲"受控元素" - 受控組件:這個組件的改變完全受控於數據的變化,數據變了,頁面變了
- 非受控組件:
input
框,我直接操作dom
,我不讓他進行數據的綁定,輸入完成點擊按鈕的時候,我直接通過refs
拿dom
上的內容來進行操作,不是通過數據來控制 - 受控組件一定是更好的。
react
是一個數據驅動的框架,所以數據驅動是react
核心,所以組件都應該被數據控制
- 函數組件和
hooks
,答案如下所示:
Hooks
技術,其作用讓函數組件變得強大起來,它可以讓react
函數組件也擁有狀態,讓我們用現有的JavaScript
技術就能快速上手,讓我們獲取數據、更改狀態是如此的輕鬆- 使用
Hook
操作數據狀態相比類組件更簡潔,這也是函數式編程的魅力 - 關於
Hooks
的內容比較多,比如常用的三個基本Hook
功能:useState、useEffect、useContext
,以及額外的方法:useRef、useReducer、useMemo、useCallback、useLayoutEffect、useDebugValue
等
this
指向問題的解決方法,答案如下所示:
- 行間定義事件後面使用
bind
綁定this
,這一種方法使用bind
來修改this
的指向,需要注意的是bind
括號內第一個參數是修改this
的,後面可以設置其他參數進行傳值,代碼如下所示:
run(){
alert("第一種方法!")
}
<button onClick={this.run.bind(this)}>第一種</button>
- 在構造函數內部聲明
this
指向,和第一種方法原理一樣,只是寫的位置不同,代碼如下所示:
constructor(props) {
super(props);
this.state={
//定義數據
}
this.run = this.run.bind(this);
}
run(){
alert("第二種方法!")
}
<button onClick={this.run}>第二種</button>
- 聲明事件時將事件等於一個箭頭函數,將定義的
run
方法再等於一個箭頭函數,利用箭頭函數沒有自己的this
指針會繼承外層的作用域這個特性,來解決this
指向問題,代碼如下所示:
run=()=> {
alert("第三種方法!")
}
<button onClick={this.run}>第三種</button>
- 行間定義事件使用箭頭函數,和第三種方法的原理是一樣的,只是寫法不同,代碼如下所示:
run(){
alert("第四種方法!")
}
<button onClick={()=>this.run()>第四種</button>
- 函數組件怎麼做性能優化,答案如下所示:
- 函數式組件性能比普通組件性能高,因爲函數組件沒有類的其他屬性,沒有構造類的過程
- 函數式組件,
props
發生變化以後,函數就會重新執行,React.memo(function Test() { return <div>123</div> })
,這樣包裝,組件就會有shoulComponentUpdate
這樣的屬性,這樣函數式組件的性能一定要不普通組件的性能要好的 - 函數式組件沒有類的構造,生命週期,直接執行就可以,代碼如下所示:
function Test() {
return <div>123</div>
}
16.在哪個生命週期裏發送ajax
,答案如下所示:
- 一定要在
componentDidMount
中去發送 componentWillMount
在新版本的react
中已經被廢棄了,取而代之的是一個getDerivedStateFromProps
這樣一個生命週期函數,所以用componentWillMount
不合適- 在用
ssr
項目中的時候,componentWillMount
要做服務端數據的獲取,所以不能被佔用
ssr
的原理是什麼,答案如下所示:
SSR
,服務端渲染,React
代碼在服務端上運行,直接生成帶有數據的HTML
頁面(ajax
請求均在服務器上完成 ),然後直接將該頁面返回給客戶端,客戶端只需解析HTML
就能展示頁面- 服務端渲染的優點,如下所示:
- 更好的
SEO
,因爲在後端有完整的HTML
頁面,所以爬蟲更容易爬取關鍵信息 - 首屏渲染速度快,用戶體驗更好
- 無需佔用客戶端資源,即解析模板的工作完全交由後端來做,客戶端只要解析標準的
HTML
頁面即可,這樣對於客戶端的資源佔用更少,尤其是移動端,也可以更省電 - 後端生成生成緩存片段,這樣就可以減少數據庫查詢浪費的時間了,且對於數據變化不大的頁面非常高效
- 更好的
- 服務端渲染的缺點,如下所示:
- 不利於前後端分離,開發效率低。使用服務器端渲染,則無法進行分工合作,則對於前端複雜度高的項目,不利於項目高效開發
- 服務器壓力變大,因爲
React
代碼由服務端執行並生成完成頁面,當外部訪問量增多,可能會出現頁面加載變慢( 請求阻塞 )等情況,此時可以通過負載均衡策略解決
- 適合
SSR
的項目,如下所示:- 項目要求
SEO
,SSR
就很合適 ( 關於SEO
,預渲染也能做到 ) - 需求項目某頁面首屏時間要求很快,
SSR
可以減少白屏時間 - 首屏頁數據請求多
- 項目要求
react
服務端渲染的流程,如下所示:- 打包階段會將業務代碼打包兩次,一份部署在服務端,一份用於客戶端(可傳到
cdn
) - 然後啓動服務,基於用戶請求的路由決定
render
哪個頁面,主要用到renderToStringapi
將page
組件轉化爲html
標記 - 最簡單的情況,將
html
標記直接返回客戶端,渲染一個靜態頁面 - 但實際業務中,一般在服務端需要獲取數據,根據數據來生成
html
,這種情況下,當在客戶端重新render
時,如何保證數據一致呢,解決辦法是將服務端獲取到的數據以字符串的形式返回給客戶端,客戶端渲染的時候直接以該數據進行渲染,保證數據的一致性,進而保證了ui
的一致性 - 當在客戶端運行時,主要用
hydrateapi
將html
標記與js
代碼重新結合,之後就與服務端完全沒關係了,可以當spa
的情況處理
- 打包階段會將業務代碼打包兩次,一份部署在服務端,一份用於客戶端(可傳到
react
中實現SSR
的核心原理,就是虛擬DOM
的存在
redux-saga
的設計思想的理解,以及sideEffects
的理解,答案如下所示:
redux
設計思想,web
應用是一個狀態機,視圖與狀態是一一對應的,所有的狀態保存在一個對象裏面saga
的應用整體思路,使用了Saga
後,react
只負責數據如何展示,redux
來負責數據的狀態和綁定數據到react
,而Saga
處理了大部分複雜的業務邏輯- 在
app.js
入口文件引入saga
以及reducers
,動態執行saga
語句middleware.run(sagas)
必須要在store
創建好之後才能執行,在store
之前執行,程序會報錯。rootsage
爲所有模塊組件的sage
集合,用sage
副作用的all
方法同時併發發出。引入action
組件,發出action
中定義好的action
,用saga
副作用處理函數takeLatest
(類似於防抖),執行最後一次請求。reducer
文件對需要更改返回state
的action
進行處理 redux-saga
就是redux
的一箇中間件,用於更優雅地管理副作用(side effects
),redux-saga
可以理解爲一個和 系統交互的 常駐進程,可簡單定義爲saga = Worker + Warcher
effect
是一個普通的javascript
對象,包含一些指令,這些指令最終會被redux-saga
中間件 解釋並執行。在redux-saga
世界裏,所有的Effect
都必須被yield
纔會執行。原則上來說,所有的yield
後面也只能跟Effect
,以保證代碼的易測性,task
是generator
方法的執行環境,所有saga
的generator
方法都跑在task
裏- 作用是用於更優雅地管理副作用, 在前端就是異步網絡請求;本質就是爲了解決異步
action
的問題 - 優點,如下所示:
- 副作用轉移到單獨的
saga.js
中,不再摻雜在action.js
中,保持action
的簡單純粹,又使得異步操作集中可以被集中處理,對比redux-thunk
redux-saga
提供了豐富的Effects
,以及sagas
的機制(所有的saga
都可以被中斷),在處理複雜的異步問題上更順手。提供了更加細膩的控制流- 對比
thunk
,dispatch
的參數依然是一個純粹的action (FSA)
- 每一個
saga
都是 一個generator function
,代碼可以採用 同步書寫 的方式 去處理 異步邏輯(No Callback Hell)
,代碼變得更易讀 - 同樣是受益於
generator function
的saga
實現,代碼異常/請求失敗 都可以直接通過try/catch
語法直接捕獲處理
- 副作用轉移到單獨的
react
,jquery
,vue
是否有可能共存在一個項目中,答案如下所示:
- 完全可以共存,關鍵看怎麼共存
- 組件是什麼?類是什麼?類被編譯成什麼,答案如下所示:
- 組件指的是頁面的一部分,用類去實現,編譯成一個構造函數
- 模塊是一個個
webpack
import
引入的文件,ES6
中的類在es5
中就是構造函數
- 你是如何跟着社區成長的,答案如下所示:
- 看英文官方文檔,去學習相應的原理
- 如何避免
ajax
數據重新獲取,答案如下所示:
- 使用
redux
,判斷數據有沒有,有的話就不要再次請求
react-router4
的核心思想是什麼,和react-router3
有什麼區別,答案如下所示:
react-router3
的路由需要在一個文件內容易配置,而react-router4
的理念則是把一個路由當做是一個組件,直接在組件中使用,這是react-router4
和react-router3
在設計理念上的不同
immutable.js
和redux
的最佳實踐,答案如下所示:
immutable
來自於函數式編程的世界,我們可以稱它爲不可變,相等性檢查將包括兩個部分,值檢查和引用檢查React
重新渲染,React
通過對組件屬性(props)
和狀態(state)
進行變更檢查以決定是否更新並重新渲染該組件,若組件狀態太過龐大,組件性能就會下降,因爲對象越複雜,其相等性檢查就會越慢。對於嵌套對象,必須迭代層層進行檢查判斷,耗費時間過長。若僅修改對象的屬性,其引用保持不變,相等性檢查中的引用檢查結果不變。Immutable
提供一直簡單快捷的方式以判斷對象是否變更,對於React
組件更新和重新渲染性能可以有較大幫助Immutable
數據,絕對不要突然修改對象,首先複製然後修改複製對象,再返回這個新對象,保持原對象不變。Immutable
數據和原生JavaScript
對象的主要差異爲持久化數據結構和結構共享,如下所示:- 持久數據結構,主張所有操作都返回該數據結構的更新副本,並保持原有結構不變,而不是改變原來的結構。通常利用
Trie
構建它不可變的持久性數據結構,它的整體結構可以看作一棵樹,一個樹節點可以對應代表對象某一個屬性,節點值即屬性值 - 結構共享,一旦創建一個
Immutable Trie
型對象,我們可以把該Trie
型對象想象成如下一棵樹,在之後的對象變更儘可能的重用樹節點。當我們要更新一個Immutable
對象的屬性值時,就是對應着需要重構該Trie
樹中的某一個節點,對於Trie
樹,我們修改某一節點只需要重構該節點及受其影響的節點,即其祖先節點,如上圖中的四個綠色節點,而其他節點可以完全重用
- 持久數據結構,主張所有操作都返回該數據結構的更新副本,並保持原有結構不變,而不是改變原來的結構。通常利用
React
組件狀態必須是一個原生JavaScript
對象,而不能是一個Immutable
對象,因爲React
的setState
方法期望接受一個對象然後使用Object.assign
方法將其與之前的狀態對象合併,如下所示:- 使用
Immutable.js
的訪問API
訪問state
,如get() , getIn()
- 使用
Immutable.js
的集合操作生成組件子元素,使用高階函數如map() , reduce()
等創React
元素的子元素 - 使用
Immutable.js
的更新操作API
更新state
- 使用
- 對於
Immutable.js
與Redux
實踐,如下所示:- JavaScript對象轉換爲Immutable對象,如下所示:
- 不要在
Immutable
對象中混用原生JavaScript
對象 - 當在
Immutable
對象內添加JavaScript
對象時,首先使用fromJS()
方法將JavaScript
對象轉換爲Immutable
對象,然後使用update() , merge() , set()
等更新API
對Immutable
對象進行更新操作
- 不要在
- Immutable與Redux state tree,如下所示:
- 使用
Immutable
對象表示完整的Redux
狀態樹,對於一個Redux
應用,完整的狀態樹應該由一個Immutable
對象表示,而沒有原生JavaScript
對象 - 使用
fromJS()
方法創建狀態樹,狀態樹對象可以是一個Immutable.Record
或者任何其他的實現了get , set , withMutations
方法的Immutable
集合的實例 - 使用
redux-immutable
庫調整combineReducers
方法使其能處理Immutable
- 使用
- Immutable與Redux組件,如下所示:
- 除了在展示型組件內,其他地方一律使用
Immutable
方式操作狀態對象。爲了保證應用性能,在容器組件,選擇器(selectors),reducer
函數,action
創建函數,sagas
和thunks
函數內等所有地方均使用Immutable
,但是不在展示型組件內使用 - 在容器組件內使用
Immutable
,容器組件可以使用react-redux
提供的connect
方法訪問redux
的store
,所以我們需要保證選擇器(selectors)
總是返回Immutable
對象,否則,將會導致不必要的重新渲染。另外,我們可以使用諸如reselect
的第三方庫緩存選擇器(selectors)
以提高部分情景下的性能
- 除了在展示型組件內,其他地方一律使用
- Immutable對象轉換爲JavaScript對象,如下所示:
- 絕對不要在
mapStateToProps
方法內使用toJS() 方法,toJS()
方法每次會調用時都是返回一個原生JavaScript
對象,如果在mapStateToProps
方法內使用toJS()
方法,則每次狀態樹(Immutable對象)
變更時,無論該toJS()
方法返回的JavaScript
對象是否實際發生改變,組件都會認爲該對象發生變更,從而導致不必要的重新渲染 - 絕對不要在展示型組件內使用
toJS()
方法,如果傳遞給某組件一個Immuatble
對象類型的prop
,則該組件的渲染取決於該Immutable
對象,這將給組件的重用,測試和重構帶來更多困難 - 當容器組件將
Immutable
類型的屬性(props)
傳入展示型組件時,需使用高階組件(HOC)
將其轉換爲原生JavaScript
對象
- 絕對不要在
- JavaScript對象轉換爲Immutable對象,如下所示:
reselect
是做什麼使用的,答案如下所示:
- 充當
vue
裏面computed
計算屬性的角 - 如果依賴的數據沒有發生變化,計算屬性就不會重新計算,做緩存提升代碼性能
reselect
的原理是,只要相關狀態不變,即直接使用上一次的緩存結果reselect
通過創建選擇器(selectors)
,該函數接受一個state
參數,然後返回我們需要在mapStateToProps
方法內返回對象的某一個數據項,一個選擇器的處理可以分爲兩個步驟,如下所示:- 接受
state
參數,根據我們提供的映射函數數組分別進行計算,如果返回結果和上次第一步的計算結果一致,說明命中緩存,則不進行第二步計算,直接返回上次第二步的計算結果,否則繼續第二步計算。第一步的結果比較,通常僅僅是===
相等性檢查,性能是足夠的 - 根據第一步返回的結果,計算,返回最終結果
- 接受
react-router
基本原理,hashHistory,browserHistory
,如下所示:
hashHistory
: 不需要後端支持,hashHistory
在#
browserHistory
:還需要後端在服務器上做配置
- 使用異步組件的情況,如下所示:
- 異步組件,懶加載,按需加載
- 如果可以,儘量在所有項目中使用,可以減小項目打包後的大小,在前端加載的時候,不會一次加載過大的js文件
Reloadable
庫,路由懶加載,按需加載,訪問那個頁面加載哪個頁面代碼
XSS
攻擊在react
中如何防範,如下所示:
react
直接解析字符串,字符串帶有script
標籤,標籤內寫有可執行的js
代碼,
儘量在創建的時候轉義- 代碼如下所示:
<div dangerouslySetInnerHTML={{ __html: '<script>alert(1)</script>' }}></div>
getDerivedStateFromProps,getSnapshotBeforeUpdate
,如下所示:
getDerivedStateFromProps和componentWillReceiveProps
差不多,可以對state
進行一些更改getSnapshotBeforeUpdate和componentWillUpdate
差不多,想要獲取更新之前的dom
結構可以用它