建設(shè)銀行網(wǎng)站百度一下百度平臺app下載
說說你用react有什么坑點(diǎn)?
1. JSX做表達(dá)式判斷時(shí)候,需要強(qiáng)轉(zhuǎn)為boolean類型
如果不使用
!!b
進(jìn)行強(qiáng)轉(zhuǎn)數(shù)據(jù)類型,會在頁面里面輸出0
。
render() {const b = 0;return <div>{!!b && <div>這是一段文本</div>}</div>
}
2. 盡量不要在 componentWillReviceProps
里使用 setState,如果一定要使用,那么需要判斷結(jié)束條件,不然會出現(xiàn)無限重渲染,導(dǎo)致頁面崩潰
3. 給組件添加ref時(shí)候,盡量不要使用匿名函數(shù),因?yàn)楫?dāng)組件更新的時(shí)候,匿名函數(shù)會被當(dāng)做新的prop處理,讓ref屬性接受到新函數(shù)的時(shí)候,react內(nèi)部會先清空ref,也就是會以null為回調(diào)參數(shù)先執(zhí)行一次ref這個(gè)props,然后在以該組件的實(shí)例執(zhí)行一次ref,所以用匿名函數(shù)做ref的時(shí)候,有的時(shí)候去ref賦值后的屬性會取到null
4. 遍歷子節(jié)點(diǎn)的時(shí)候,不要用 index 作為組件的 key 進(jìn)行傳入
React中的props為什么是只讀的?
this.props
是組件之間溝通的一個(gè)接口,原則上來講,它只能從父組件流向子組件。React具有濃重的函數(shù)式編程的思想。
提到函數(shù)式編程就要提一個(gè)概念:純函數(shù)。它有幾個(gè)特點(diǎn):
- 給定相同的輸入,總是返回相同的輸出。
- 過程沒有副作用。
- 不依賴外部狀態(tài)。
this.props
就是汲取了純函數(shù)的思想。props的不可以變性就保證的相同的輸入,頁面顯示的內(nèi)容是一樣的,并且不會產(chǎn)生副作用
高階組件存在的問題
- 靜態(tài)方法丟失(必須將靜態(tài)方法做拷貝)
refs
?屬性不能透傳(如果你向一個(gè)由高階組件創(chuàng)建的組件的元素添加ref
引用,那么ref
指向的是最外層容器組件實(shí)例的,而不是被包裹的WrappedComponent
組件。)- 反向繼承不能保證完整的子組件樹被解析
React 組件有兩種形式,分別是 class 類型和 function 類型(無狀態(tài)組件)。
我們知道反向繼承的渲染劫持可以控制?WrappedComponent
?的渲染過程,也就是說這個(gè)過程中我們可以對?elements tree
、?state
、?props
?或?render()
?的結(jié)果做各種操作。
但是如果渲染?elements tree
?中包含了 function 類型的組件的話,這時(shí)候就不能操作組件的子組件了。
對有狀態(tài)組件和無狀態(tài)組件的理解及使用場景
(1)有狀態(tài)組件
特點(diǎn):
- 是類組件
- 有繼承
- 可以使用this
- 可以使用react的生命周期
- 使用較多,容易頻繁觸發(fā)生命周期鉤子函數(shù),影響性能
- 內(nèi)部使用 state,維護(hù)自身狀態(tài)的變化,有狀態(tài)組件根據(jù)外部組件傳入的 props 和自身的 state進(jìn)行渲染。
使用場景:
- 需要使用到狀態(tài)的。
- 需要使用狀態(tài)操作組件的(無狀態(tài)組件的也可以實(shí)現(xiàn)新版本react hooks也可實(shí)現(xiàn))
總結(jié): 類組件可以維護(hù)自身的狀態(tài)變量,即組件的 state ,類組件還有不同的生命周期方法,可以讓開發(fā)者能夠在組件的不同階段(掛載、更新、卸載),對組件做更多的控制。類組件則既可以充當(dāng)無狀態(tài)組件,也可以充當(dāng)有狀態(tài)組件。當(dāng)一個(gè)類組件不需要管理自身狀態(tài)時(shí),也可稱為無狀態(tài)組件。
(2)無狀態(tài)組件 特點(diǎn):
- 不依賴自身的狀態(tài)state
- 可以是類組件或者函數(shù)組件。
- 可以完全避免使用 this 關(guān)鍵字。(由于使用的是箭頭函數(shù)事件無需綁定)
- 有更高的性能。當(dāng)不需要使用生命周期鉤子時(shí),應(yīng)該首先使用無狀態(tài)函數(shù)組件
- 組件內(nèi)部不維護(hù) state ,只根據(jù)外部組件傳入的 props 進(jìn)行渲染的組件,當(dāng) props 改變時(shí),組件重新渲染。
使用場景:
- 組件不需要管理 state,純展示
優(yōu)點(diǎn):
- 簡化代碼、專注于 render
- 組件不需要被實(shí)例化,無生命周期,提升性能。 輸出(渲染)只取決于輸入(屬性),無副作用
- 視圖和數(shù)據(jù)的解耦分離
缺點(diǎn):
- 無法使用 ref
- 無生命周期方法
- 無法控制組件的重渲染,因?yàn)闊o法使用shouldComponentUpdate 方法,當(dāng)組件接受到新的屬性時(shí)則會重渲染
總結(jié): 組件內(nèi)部狀態(tài)且與外部無關(guān)的組件,可以考慮用狀態(tài)組件,這樣狀態(tài)樹就不會過于復(fù)雜,易于理解和管理。當(dāng)一個(gè)組件不需要管理自身狀態(tài)時(shí),也就是無狀態(tài)組件,應(yīng)該優(yōu)先設(shè)計(jì)為函數(shù)組件。比如自定義的 <Button/>
、 <Input />
等組件。
什么是受控組件和非受控組件
- 受狀態(tài)控制的組件,必須要有onChange方法,否則不能使用 受控組件可以賦予默認(rèn)值(官方推薦使用 受控組件) 實(shí)現(xiàn)雙向數(shù)據(jù)綁定
class Input extends Component{constructor(){super();this.state = {val:'100'}}handleChange = (e) =>{ //e是事件源let val = e.target.value;this.setState({val});};render(){return (<div><input type="text" value={this.state.val} onChange={this.handleChange}/>{this.state.val}</div>)}
}
- 非受控也就意味著我可以不需要設(shè)置它的state屬性,而通過ref來操作真實(shí)的DOM
class Sum extends Component{constructor(){super();this.state = {result:''}}//通過ref設(shè)置的屬性 可以通過this.refs獲取到對應(yīng)的dom元素handleChange = () =>{let result = this.refs.a.value + this.b.value;this.setState({result});};render(){return (<div onChange={this.handleChange}><input type="number" ref="a"/>{/*x代表的真實(shí)的dom,把元素掛載在了當(dāng)前實(shí)例上*/}<input type="number" ref={(x)=>{this.b = x;}}/>{this.state.result}</div>)}
}
React 16中新生命周期有哪些
關(guān)于 React16 開始應(yīng)用的新生命周期: 可以看出,React16 自上而下地對生命周期做了另一種維度的解讀:
- Render 階段:用于計(jì)算一些必要的狀態(tài)信息。這個(gè)階段可能會被 React 暫停,這一點(diǎn)和 React16 引入的 Fiber 架構(gòu)(我們后面會重點(diǎn)講解)是有關(guān)的;
- Pre-commit階段:所謂“commit”,這里指的是“更新真正的 DOM 節(jié)點(diǎn)”這個(gè)動作。所謂 Pre-commit,就是說我在這個(gè)階段其實(shí)還并沒有去更新真實(shí)的 DOM,不過 DOM 信息已經(jīng)是可以讀取的了;
- Commit 階段:在這一步,React 會完成真實(shí) DOM 的更新工作。Commit 階段,我們可以拿到真實(shí) DOM(包括 refs)。
與此同時(shí),新的生命周期在流程方面,仍然遵循“掛載”、“更新”、“卸載”這三個(gè)廣義的劃分方式。它們分別對應(yīng)到:
- 掛載過程:
- constructor
- getDerivedStateFromProps
- render
- componentDidMount
- 更新過程:
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- componentDidUpdate
- 卸載過程:
- componentWillUnmount
參考 前端進(jìn)階面試題詳細(xì)解答
React中什么是受控組件和非控組件?
(1)受控組件 在使用表單來收集用戶輸入時(shí),例如<input><select><textearea>
等元素都要綁定一個(gè)change事件,當(dāng)表單的狀態(tài)發(fā)生變化,就會觸發(fā)onChange事件,更新組件的state。這種組件在React中被稱為受控組件,在受控組件中,組件渲染出的狀態(tài)與它的value或checked屬性相對應(yīng),react通過這種方式消除了組件的局部狀態(tài),使整個(gè)狀態(tài)可控。react官方推薦使用受控表單組件。
受控組件更新state的流程:
- 可以通過初始state中設(shè)置表單的默認(rèn)值
- 每當(dāng)表單的值發(fā)生變化時(shí),調(diào)用onChange事件處理器
- 事件處理器通過事件對象e拿到改變后的狀態(tài),并更新組件的state
- 一旦通過setState方法更新state,就會觸發(fā)視圖的重新渲染,完成表單組件的更新
受控組件缺陷: 表單元素的值都是由React組件進(jìn)行管理,當(dāng)有多個(gè)輸入框,或者多個(gè)這種組件時(shí),如果想同時(shí)獲取到全部的值就必須每個(gè)都要編寫事件處理函數(shù),這會讓代碼看著很臃腫,所以為了解決這種情況,出現(xiàn)了非受控組件。
(2)非受控組件 如果一個(gè)表單組件沒有value props(單選和復(fù)選按鈕對應(yīng)的是checked props)時(shí),就可以稱為非受控組件。在非受控組件中,可以使用一個(gè)ref來從DOM獲得表單值。而不是為每個(gè)狀態(tài)更新編寫一個(gè)事件處理程序。
React官方的解釋:
要編寫一個(gè)非受控組件,而不是為每個(gè)狀態(tài)更新都編寫數(shù)據(jù)處理函數(shù),你可以使用 ref來從 DOM 節(jié)點(diǎn)中獲取表單數(shù)據(jù)。
因?yàn)榉鞘芸亟M件將真實(shí)數(shù)據(jù)儲存在 DOM 節(jié)點(diǎn)中,所以在使用非受控組件時(shí),有時(shí)候反而更容易同時(shí)集成 React 和非 React 代碼。如果你不介意代碼美觀性,并且希望快速編寫代碼,使用非受控組件往往可以減少你的代碼量。否則,你應(yīng)該使用受控組件。
例如,下面的代碼在非受控組件中接收單個(gè)屬性:
class NameForm extends React.Component {constructor(props) {super(props);this.handleSubmit = this.handleSubmit.bind(this);}handleSubmit(event) {alert('A name was submitted: ' + this.input.value);event.preventDefault();}render() {return (<form onSubmit={this.handleSubmit}><label>Name: <input type="text" ref={(input) => this.input = input} /> </label><input type="submit" value="Submit" /></form>);}
}
總結(jié): 頁面中所有輸入類的DOM如果是現(xiàn)用現(xiàn)取的稱為非受控組件,而通過setState將輸入的值維護(hù)到了state中,需要時(shí)再從state中取出,這里的數(shù)據(jù)就受到了state的控制,稱為受控組件。
Redux 中異步的請求怎么處理
可以在 componentDidmount 中直接進(jìn)?請求?須借助redux。但是在?定規(guī)模的項(xiàng)?中,上述?法很難進(jìn)?異步流的管理,通常情況下我們會借助redux的異步中間件進(jìn)?異步處理。redux異步流中間件其實(shí)有很多,當(dāng)下主流的異步中間件有兩種redux-thunk、redux-saga。
(1)使用react-thunk中間件
redux-thunk優(yōu)點(diǎn):
- 體積?: redux-thunk的實(shí)現(xiàn)?式很簡單,只有不到20?代碼
- 使?簡單: redux-thunk沒有引?像redux-saga或者redux-observable額外的范式,上?簡單
redux-thunk缺陷:
- 樣板代碼過多: 與redux本身?樣,通常?個(gè)請求需要?量的代碼,?且很多都是重復(fù)性質(zhì)的
- 耦合嚴(yán)重: 異步操作與redux的action偶合在?起,不?便管理
- 功能孱弱: 有?些實(shí)際開發(fā)中常?的功能需要??進(jìn)?封裝
使用步驟:
- 配置中間件,在store的創(chuàng)建中配置
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk'// 設(shè)置調(diào)試工具
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
// 設(shè)置中間件
const enhancer = composeEnhancers(applyMiddleware(thunk)
);const store = createStore(reducer, enhancer);export default store;
- 添加一個(gè)返回函數(shù)的actionCreator,將異步請求邏輯放在里面
/** 發(fā)送get請求,并生成相應(yīng)action,更新store的函數(shù) @param url {string} 請求地址 @param func {function} 真正需要生成的action對應(yīng)的actionCreator @return {function} */
// dispatch為自動接收的store.dispatch函數(shù)
export const getHttpAction = (url, func) => (dispatch) => {axios.get(url).then(function(res){const action = func(res.data)dispatch(action)})
}
- 生成action,并發(fā)送action
componentDidMount(){var action = getHttpAction('/getData', getInitTodoItemAction)// 發(fā)送函數(shù)類型的action時(shí),該action的函數(shù)體會自動執(zhí)行store.dispatch(action)
}
(2)使用redux-saga中間件
redux-saga優(yōu)點(diǎn):
- 異步解耦: 異步操作被被轉(zhuǎn)移到單獨(dú) saga.js 中,不再是摻雜在 action.js 或 component.js 中
- action擺脫thunk function: dispatch 的參數(shù)依然是?個(gè)純粹的 action (FSA),?不是充滿 “?魔法” thunk function
- 異常處理: 受益于 generator function 的 saga 實(shí)現(xiàn),代碼異常/請求失敗 都可以直接通過 try/catch 語法直接捕獲處理
- 功能強(qiáng)?: redux-saga提供了?量的Saga 輔助函數(shù)和Effect 創(chuàng)建器供開發(fā)者使?,開發(fā)者?須封裝或者簡單封裝即可使?
- 靈活: redux-saga可以將多個(gè)Saga可以串?/并?組合起來,形成?個(gè)?常實(shí)?的異步flow
- 易測試,提供了各種case的測試?案,包括mock task,分?覆蓋等等
redux-saga缺陷:
- 額外的學(xué)習(xí)成本: redux-saga不僅在使?難以理解的 generator function,?且有數(shù)?個(gè)API,學(xué)習(xí)成本遠(yuǎn)超redux-thunk,最重要的是你的額外學(xué)習(xí)成本是只服務(wù)于這個(gè)庫的,與redux-observable不同,redux-observable雖然也有額外學(xué)習(xí)成本但是背后是rxjs和?整套思想
- 體積龐?: 體積略?,代碼近2000?,min版25KB左右
- 功能過剩: 實(shí)際上并發(fā)控制等功能很難?到,但是我們依然需要引?這些代碼
- ts?持不友好: yield?法返回TS類型
redux-saga可以捕獲action,然后執(zhí)行一個(gè)函數(shù),那么可以把異步代碼放在這個(gè)函數(shù)中,使用步驟如下:
- 配置中間件
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga'
import TodoListSaga from './sagas'const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const sagaMiddleware = createSagaMiddleware()const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware)
);const store = createStore(reducer, enhancer);
sagaMiddleware.run(TodoListSaga)export default store;
- 將異步請求放在sagas.js中
import {takeEvery, put} from 'redux-saga/effects'
import {initTodoList} from './actionCreator'
import {GET_INIT_ITEM} from './actionTypes'
import axios from 'axios'function* func(){try{// 可以獲取異步返回?cái)?shù)據(jù)const res = yield axios.get('/getData')const action = initTodoList(res.data)// 將action發(fā)送到reduceryield put(action)}catch(e){console.log('網(wǎng)絡(luò)請求失敗')}
}function* mySaga(){// 自動捕獲GET_INIT_ITEM類型的action,并執(zhí)行funcyield takeEvery(GET_INIT_ITEM, func)
}export default mySaga
- 發(fā)送action
componentDidMount(){const action = getInitTodoItemAction()store.dispatch(action)
}
對 React-Intl 的理解,它的工作原理?
React-intl是雅虎的語言國際化開源項(xiàng)目FormatJS的一部分,通過其提供的組件和API可以與ReactJS綁定。
React-intl提供了兩種使用方法,一種是引用React組件,另一種是直接調(diào)取API,官方更加推薦在React項(xiàng)目中使用前者,只有在無法使用React組件的地方,才應(yīng)該調(diào)用框架提供的API。它提供了一系列的React組件,包括數(shù)字格式化、字符串格式化、日期格式化等。
在React-intl中,可以配置不同的語言包,他的工作原理就是根據(jù)需要,在語言包之間進(jìn)行切換。
對 React context 的理解
在React中,數(shù)據(jù)傳遞一般使用props傳遞數(shù)據(jù),維持單向數(shù)據(jù)流,這樣可以讓組件之間的關(guān)系變得簡單且可預(yù)測,但是單項(xiàng)數(shù)據(jù)流在某些場景中并不適用。單純一對的父子組件傳遞并無問題,但要是組件之間層層依賴深入,props就需要層層傳遞顯然,這樣做太繁瑣了。
Context 提供了一種在組件之間共享此類值的方式,而不必顯式地通過組件樹的逐層傳遞 props。
可以把context當(dāng)做是特定一個(gè)組件樹內(nèi)共享的store,用來做數(shù)據(jù)傳遞。簡單說就是,當(dāng)你不想在組件樹中通過逐層傳遞props或者state的方式來傳遞數(shù)據(jù)時(shí),可以使用Context來實(shí)現(xiàn)跨層級的組件數(shù)據(jù)傳遞。
JS的代碼塊在執(zhí)行期間,會創(chuàng)建一個(gè)相應(yīng)的作用域鏈,這個(gè)作用域鏈記錄著運(yùn)行時(shí)JS代碼塊執(zhí)行期間所能訪問的活動對象,包括變量和函數(shù),JS程序通過作用域鏈訪問到代碼塊內(nèi)部或者外部的變量和函數(shù)。
假如以JS的作用域鏈作為類比,React組件提供的Context對象其實(shí)就好比一個(gè)提供給子組件訪問的作用域,而 Context對象的屬性可以看成作用域上的活動對象。由于組件 的 Context 由其父節(jié)點(diǎn)鏈上所有組件通 過 getChildContext()返回的Context對象組合而成,所以,組件通過Context是可以訪問到其父組件鏈上所有節(jié)點(diǎn)組件提供的Context的屬性。
為什么React并不推薦優(yōu)先考慮使用Context?
- Context目前還處于實(shí)驗(yàn)階段,可能會在后面的發(fā)行版本中有很大的變化,事實(shí)上這種情況已經(jīng)發(fā)生了,所以為了避免給今后升級帶來大的影響和麻煩,不建議在app中使用context。
- 盡管不建議在app中使用context,但是獨(dú)有組件而言,由于影響范圍小于app,如果可以做到高內(nèi)聚,不破壞組件樹之間的依賴關(guān)系,可以考慮使用context
- 對于組件之間的數(shù)據(jù)通信或者狀態(tài)管理,有效使用props或者state解決,然后再考慮使用第三方的成熟庫進(jìn)行解決,以上的方法都不是最佳的方案的時(shí)候,在考慮context。
- context的更新需要通過setState()觸發(fā),但是這并不是很可靠的,Context支持跨組件的訪問,但是如果中間的子組件通過一些方法不影響更新,比如 shouldComponentUpdate() 返回false 那么不能保證Context的更新一定可以使用Context的子組件,因此,Context的可靠性需要關(guān)注
React中refs的作用是什么?有哪些應(yīng)用場景?
Refs 提供了一種方式,用于訪問在 render 方法中創(chuàng)建的 React 元素或 DOM 節(jié)點(diǎn)。Refs 應(yīng)該謹(jǐn)慎使用,如下場景使用 Refs 比較適合:
- 處理焦點(diǎn)、文本選擇或者媒體的控制
- 觸發(fā)必要的動畫
- 集成第三方 DOM 庫
Refs 是使用 React.createRef()
方法創(chuàng)建的,他通過 ref
屬性附加到 React 元素上。要在整個(gè)組件中使用 Refs,需要將 ref
在構(gòu)造函數(shù)中分配給其實(shí)例屬性:
class MyComponent extends React.Component {constructor(props) {super(props)this.myRef = React.createRef()}render() {return <div ref={this.myRef} />}
}
由于函數(shù)組件沒有實(shí)例,因此不能在函數(shù)組件上直接使用 ref
:
function MyFunctionalComponent() {return <input />;
}
class Parent extends React.Component {constructor(props) {super(props);this.textInput = React.createRef();}render() {// 這將不會工作!return (<MyFunctionalComponent ref={this.textInput} />);}
}
但可以通過閉合的幫助在函數(shù)組件內(nèi)部進(jìn)行使用 Refs:
function CustomTextInput(props) {// 這里必須聲明 textInput,這樣 ref 回調(diào)才可以引用它let textInput = null;function handleClick() {textInput.focus();}return (<div><inputtype="text"ref={(input) => { textInput = input; }} /> <inputtype="button"value="Focus the text input"onClick={handleClick}/></div>);
}
注意:
- 不應(yīng)該過度的使用 Refs
ref
的返回值取決于節(jié)點(diǎn)的類型:- 當(dāng)
ref
屬性被用于一個(gè)普通的 HTML 元素時(shí),React.createRef()
將接收底層 DOM 元素作為他的current
屬性以創(chuàng)建ref
。 - 當(dāng)
ref
屬性被用于一個(gè)自定義的類組件時(shí),ref
對象將接收該組件已掛載的實(shí)例作為他的current
。
- 當(dāng)
- 當(dāng)在父組件中需要訪問子組件中的
ref
時(shí)可使用傳遞 Refs 或回調(diào) Refs。
React聲明組件有哪幾種方法,有什么不同?
React 聲明組件的三種方式:
- 函數(shù)式定義的
無狀態(tài)組件
- ES5原生方式
React.createClass
定義的組件 - ES6形式的
extends React.Component
定義的組件
(1)無狀態(tài)函數(shù)式組件 它是為了創(chuàng)建純展示組件,這種組件只負(fù)責(zé)根據(jù)傳入的props來展示,不涉及到state狀態(tài)的操作
組件不會被實(shí)例化,整體渲染性能得到提升,不能訪問this對象,不能訪問生命周期的方法
(2)ES5 原生方式 React.createClass // RFC React.createClass會自綁定函數(shù)方法,導(dǎo)致不必要的性能開銷,增加代碼過時(shí)的可能性。
(3)E6繼承形式 React.Component // RCC 目前極為推薦的創(chuàng)建有狀態(tài)組件的方式,最終會取代React.createClass形式;相對于 React.createClass可以更好實(shí)現(xiàn)代碼復(fù)用。
無狀態(tài)組件相對于于后者的區(qū)別: 與無狀態(tài)組件相比,React.createClass和React.Component都是創(chuàng)建有狀態(tài)的組件,這些組件是要被實(shí)例化的,并且可以訪問組件的生命周期方法。
React.createClass與React.Component區(qū)別:
① 函數(shù)this自綁定
- React.createClass創(chuàng)建的組件,其每一個(gè)成員函數(shù)的this都有React自動綁定,函數(shù)中的this會被正確設(shè)置。
- React.Component創(chuàng)建的組件,其成員函數(shù)不會自動綁定this,需要開發(fā)者手動綁定,否則this不能獲取當(dāng)前組件實(shí)例對象。
② 組件屬性類型propTypes及其默認(rèn)props屬性defaultProps配置不同
- React.createClass在創(chuàng)建組件時(shí),有關(guān)組件props的屬性類型及組件默認(rèn)的屬性會作為組件實(shí)例的屬性來配置,其中defaultProps是使用getDefaultProps的方法來獲取默認(rèn)組件屬性的
- React.Component在創(chuàng)建組件時(shí)配置這兩個(gè)對應(yīng)信息時(shí),他們是作為組件類的屬性,不是組件實(shí)例的屬性,也就是所謂的類的靜態(tài)屬性來配置的。
③ 組件初始狀態(tài)state的配置不同
- React.createClass創(chuàng)建的組件,其狀態(tài)state是通過getInitialState方法來配置組件相關(guān)的狀態(tài);
- React.Component創(chuàng)建的組件,其狀態(tài)state是在constructor中像初始化組件屬性一樣聲明的。
React中發(fā)起網(wǎng)絡(luò)請求應(yīng)該在哪個(gè)生命周期中進(jìn)行?為什么?
對于異步請求,最好放在componentDidMount中去操作,對于同步的狀態(tài)改變,可以放在componentWillMount中,一般用的比較少。
如果認(rèn)為在componentWillMount里發(fā)起請求能提早獲得結(jié)果,這種想法其實(shí)是錯(cuò)誤的,通常componentWillMount比componentDidMount早不了多少微秒,網(wǎng)絡(luò)上任何一點(diǎn)延遲,這一點(diǎn)差異都可忽略不計(jì)。
react的生命周期: constructor() -> componentWillMount() -> render() -> componentDidMount()
上面這些方法的調(diào)用是有次序的,由上而下依次調(diào)用。
- constructor被調(diào)用是在組件準(zhǔn)備要掛載的最開始,此時(shí)組件尚未掛載到網(wǎng)頁上。
- componentWillMount方法的調(diào)用在constructor之后,在render之前,在這方法里的代碼調(diào)用setState方法不會觸發(fā)重新render,所以它一般不會用來作加載數(shù)據(jù)之用。
- componentDidMount方法中的代碼,是在組件已經(jīng)完全掛載到網(wǎng)頁上才會調(diào)用被執(zhí)行,所以可以保證數(shù)據(jù)的加載。此外,在這方法中調(diào)用setState方法,會觸發(fā)重新渲染。所以,官方設(shè)計(jì)這個(gè)方法就是用來加載外部數(shù)據(jù)用的,或處理其他的副作用代碼。與組件上的數(shù)據(jù)無關(guān)的加載,也可以在constructor里做,但constructor是做組件state初紿化工作,并不是做加載數(shù)據(jù)這工作的,constructor里也不能setState,還有加載的時(shí)間太長或者出錯(cuò),頁面就無法加載出來。所以有副作用的代碼都會集中在componentDidMount方法里。
總結(jié):
- 跟服務(wù)器端渲染(同構(gòu))有關(guān)系,如果在componentWillMount里面獲取數(shù)據(jù),fetch data會執(zhí)行兩次,一次在服務(wù)器端一次在客戶端。在componentDidMount中可以解決這個(gè)問題,componentWillMount同樣也會render兩次。
- 在componentWillMount中fetch data,數(shù)據(jù)一定在render后才能到達(dá),如果忘記了設(shè)置初始狀態(tài),用戶體驗(yàn)不好。
- react16.0以后,componentWillMount可能會被執(zhí)行多次。
在React中組件的props改變時(shí)更新組件的有哪些方法?
在一個(gè)組件傳入的props更新時(shí)重新渲染該組件常用的方法是在componentWillReceiveProps
中將新的props更新到組件的state中(這種state被成為派生狀態(tài)(Derived State)),從而實(shí)現(xiàn)重新渲染。React 16.3中還引入了一個(gè)新的鉤子函數(shù)getDerivedStateFromProps
來專門實(shí)現(xiàn)這一需求。
(1)componentWillReceiveProps(已廢棄)
在react的componentWillReceiveProps(nextProps)生命周期中,可以在子組件的render函數(shù)執(zhí)行前,通過this.props獲取舊的屬性,通過nextProps獲取新的props,對比兩次props是否相同,從而更新子組件自己的state。
這樣的好處是,可以將數(shù)據(jù)請求放在這里進(jìn)行執(zhí)行,需要傳的參數(shù)則從componentWillReceiveProps(nextProps)中獲取。而不必將所有的請求都放在父組件中。于是該請求只會在該組件渲染時(shí)才會發(fā)出,從而減輕請求負(fù)擔(dān)。
(2)getDerivedStateFromProps(16.3引入)
這個(gè)生命周期函數(shù)是為了替代componentWillReceiveProps
存在的,所以在需要使用componentWillReceiveProps
時(shí),就可以考慮使用getDerivedStateFromProps
來進(jìn)行替代。
兩者的參數(shù)是不相同的,而getDerivedStateFromProps
是一個(gè)靜態(tài)函數(shù),也就是這個(gè)函數(shù)不能通過this訪問到class的屬性,也并不推薦直接訪問屬性。而是應(yīng)該通過參數(shù)提供的nextProps以及prevState來進(jìn)行判斷,根據(jù)新傳入的props來映射到state。
需要注意的是,如果props傳入的內(nèi)容不需要影響到你的state,那么就需要返回一個(gè)null,這個(gè)返回值是必須的,所以盡量將其寫到函數(shù)的末尾:
static getDerivedStateFromProps(nextProps, prevState) {const {type} = nextProps;// 當(dāng)傳入的type發(fā)生變化的時(shí)候,更新stateif (type !== prevState.type) {return {type,};}// 否則,對于state不進(jìn)行任何操作return null;
}
解釋 React 中 render() 的目的。
每個(gè)React組件強(qiáng)制要求必須有一個(gè) render()。它返回一個(gè) React 元素,是原生 DOM 組件的表示。如果需要渲染多個(gè) HTML 元素,則必須將它們組合在一個(gè)封閉標(biāo)記內(nèi),例如 <form>
、<group>
、<div>
等。此函數(shù)必須保持純凈,即必須每次調(diào)用時(shí)都返回相同的結(jié)果。
哪些方法會觸發(fā) React 重新渲染?重新渲染 render 會做些什么?
(1)哪些方法會觸發(fā) react 重新渲染?
- setState()方法被調(diào)用
setState 是 React 中最常用的命令,通常情況下,執(zhí)行 setState 會觸發(fā) render。但是這里有個(gè)點(diǎn)值得關(guān)注,執(zhí)行 setState 的時(shí)候不一定會重新渲染。當(dāng) setState 傳入 null 時(shí),并不會觸發(fā) render。
class App extends React.Component {state = {a: 1};render() {console.log("render");return (<React.Fragement><p>{this.state.a}</p><buttononClick={() => { this.setState({ a: 1 }); // 這里并沒有改變 a 的值 }} > Click me </button><button onClick={() => this.setState(null)}>setState null</button><Child /></React.Fragement>);}
}
- 父組件重新渲染
只要父組件重新渲染了,即使傳入子組件的 props 未發(fā)生變化,那么子組件也會重新渲染,進(jìn)而觸發(fā) render
(2)重新渲染 render 會做些什么?
- 會對新舊 VNode 進(jìn)行對比,也就是我們所說的Diff算法。
- 對新舊兩棵樹進(jìn)行一個(gè)深度優(yōu)先遍歷,這樣每一個(gè)節(jié)點(diǎn)都會一個(gè)標(biāo)記,在到深度遍歷的時(shí)候,每遍歷到一和個(gè)節(jié)點(diǎn),就把該節(jié)點(diǎn)和新的節(jié)點(diǎn)樹進(jìn)行對比,如果有差異就放到一個(gè)對象里面
- 遍歷差異對象,根據(jù)差異的類型,根據(jù)對應(yīng)對規(guī)則更新VNode
React 的處理 render 的基本思維模式是每次一有變動就會去重新渲染整個(gè)應(yīng)用。在 Virtual DOM 沒有出現(xiàn)之前,最簡單的方法就是直接調(diào)用 innerHTML。Virtual DOM厲害的地方并不是說它比直接操作 DOM 快,而是說不管數(shù)據(jù)怎么變,都會盡量以最小的代價(jià)去更新 DOM。React 將 render 函數(shù)返回的虛擬 DOM 樹與老的進(jìn)行比較,從而確定 DOM 要不要更新、怎么更新。當(dāng) DOM 樹很大時(shí),遍歷兩棵樹進(jìn)行各種比對還是相當(dāng)耗性能的,特別是在頂層 setState 一個(gè)微小的修改,默認(rèn)會去遍歷整棵樹。盡管 React 使用高度優(yōu)化的 Diff 算法,但是這個(gè)過程仍然會損耗性能.
React.Children.map和js的map有什么區(qū)別?
JavaScript中的map不會對為null或者undefined的數(shù)據(jù)進(jìn)行處理,而React.Children.map中的map可以處理React.Children為null或者undefined的情況。
useEffect和useLayoutEffect的區(qū)別
useEffect
基本上90%的情況下,都應(yīng)該用這個(gè),這個(gè)是在render結(jié)束后,你的callback函數(shù)執(zhí)行,但是不會block browser painting,算是某種異步的方式吧,但是class的componentDidMount 和componentDidUpdate是同步的,在render結(jié)束后就運(yùn)行,useEffect在大部分場景下都比class的方式性能更好.
useLayoutEffect
這個(gè)是用在處理DOM的時(shí)候,當(dāng)你的useEffect里面的操作需要處理DOM,并且會改變頁面的樣式,就需要用這個(gè),否則可能會出現(xiàn)出現(xiàn)閃屏問題, useLayoutEffect里面的callback函數(shù)會在**DOM更新完成后立即執(zhí)行,但是會在瀏覽器進(jìn)行任何繪制之前運(yùn)行完成,**阻塞了瀏覽器的繪制.
React中constructor和getInitialState的區(qū)別?
兩者都是用來初始化state的。前者是ES6中的語法,后者是ES5中的語法,新版本的React中已經(jīng)廢棄了該方法。
getInitialState是ES5中的方法,如果使用createClass方法創(chuàng)建一個(gè)Component組件,可以自動調(diào)用它的getInitialState方法來獲取初始化的State對象,
var APP = React.creatClass ({getInitialState() {return { userName: 'hi',userId: 0};}
})
React在ES6的實(shí)現(xiàn)中去掉了getInitialState這個(gè)hook函數(shù),規(guī)定state在constructor中實(shí)現(xiàn),如下:
Class App extends React.Component{constructor(props){super(props);this.state={};}}