React面試題
前端
1. 什么是React?
React是用于構(gòu)建用戶界面的JavaScript庫 , 起源于Facebook的內(nèi)部項目,該公司對市場上所有 JavaScript MVC框架都不滿意,決定自行開發(fā)一套,用于架設(shè)自己的網(wǎng)站 ,React 不是一個 MVC 框架,僅僅是視圖(V)層的庫
2. 為什么虛擬 dom 會提高性能?
虛擬 dom 相當(dāng)于在 js 和真實 dom 中間加了一個緩存,利用 dom diff 算法避免了沒有必要的 dom 操作,從而提高性能。
用Js對象表示真實的DOM結(jié)構(gòu),當(dāng)狀態(tài)變化的時候在重新創(chuàng)建一個虛擬DOM樹結(jié)構(gòu),然后用新的樹和舊的樹進行比較,記錄兩棵樹差異,把所記錄的差異應(yīng)用到所構(gòu)建的真正的 DOM 樹上,視圖就更新了。
3. 什么是JSX?
jsx是JavaScript的一種語法擴展,它跟模板語言很接近,但是它充分具備JavaScript的能力
JSX就是用來聲明React當(dāng)中的元素,React使用JSX來描述用戶界面
JSX語法糖允許前端開發(fā)者使用我們最熟悉的類HTML標(biāo)簽語法來創(chuàng)建虛擬DOM在降低學(xué)習(xí)成本
4. React創(chuàng)建元素的方法?
React.createElement()
5. class組件和函數(shù)組件區(qū)別
語法上:
函數(shù)式組件是一個純函數(shù),它是需要接受props參數(shù)并且返回一個React元素就可以了。類組件是需要繼承React.Component的,而且class組件需要創(chuàng)建render并且返回React元素,語法上來講更復(fù)雜。
狀態(tài)管理:
函數(shù)式組件沒有狀態(tài)管理,類組件有狀態(tài)管理。
調(diào)用方式:
函數(shù)式組件可以直接調(diào)用,返回一個新的React元素;類組件在調(diào)用時是需要創(chuàng)建一個實例的,然后通過調(diào)用實例里的render方法來返回一個React元素。
6. React 事件綁定的方式
React 事件綁定屬性的命名采用駝峰式寫法, 采用 JSX 的語法傳入一個函數(shù)作為事件處理函數(shù)
事件綁定函數(shù)的方式
1. 直接寫函數(shù)名字{callback},
2. 可以使用bind方法綁定調(diào)用 {callback.bind(this)}
7. 事件處理方法this指向改變
當(dāng)我們把事件函數(shù)寫成普通函數(shù)的形式時 , 調(diào)用函數(shù)使用state變量會報錯,提示state變量不存在,
是因為
事件處理程序的函數(shù)式函數(shù)調(diào)用模式,在嚴(yán)格模式下,this指向
undefinedrender函數(shù)是被組件實例調(diào)用的,因此render函數(shù)中的this指向當(dāng)前組件
解決方法: 1. 把普通函數(shù)改成箭頭函數(shù) 2. 調(diào)用函數(shù)的時候使用bind方法改變this指向
8. React事件處理方法傳值
-
調(diào)用的時候定義一個箭頭函數(shù) 函數(shù)中調(diào)用方法傳遞參數(shù)據(jù)
<button onClick={()=> this.del(index) }> 點擊 </button>-
第二種方法 bind方法傳遞參數(shù)
<button onClick={this.del.bind(this,index) }> 點擊 </button>
-
9 React如何獲取表單數(shù)據(jù)
- 給文本框綁定value屬性,value屬性綁定state中定義的變量
- 給表單綁定onChange事件,調(diào)用定義的方法
- 在方法中我們獲取e.target.value屬性,賦給value屬性綁定的變量
10. React條件渲染方法有哪些
- if-else的條件渲染方法
- 三元運算符進行條件渲染,可以縮短代碼量
- switch的多條件渲染效果
- HOC條件渲染
11. React怎么實現(xiàn)列表渲染
react中可以使用map方法渲染列表,return對應(yīng)的頁面結(jié)構(gòu)即可, React 在渲染列表時,會要求開發(fā)者為每一個列表元素指定唯一的 key ,我們盡量不要使用index索引值作為key,如果對數(shù)據(jù)進行:逆序添加、逆序刪除等破壞順序操作:可能會引起頁面更新錯誤問題。
12. React中key的作用是什么?
key是虛擬DOM對象的唯一標(biāo)識,在更新顯示時key起到極其重要的作用 ,簡單的來說就是為了提高diff的同級比較的效率,避免原地復(fù)用帶來的副作用
react采用的是自頂而下的更新策略,每次小的改動都會生成一個全新的的vdom,從而進行diff,如果不寫key,就會發(fā)生本來應(yīng)該更新卻沒有更新
13. React組件樣式的定義方式
-
外聯(lián)樣式
定義css文件,在組件中通過import導(dǎo)入css樣式,
import "App.css"
-
內(nèi)聯(lián)樣式
React推崇的是內(nèi)聯(lián)的方式定義樣式。這樣做的目的就在于讓你的組件更加的容易復(fù)用
定義一個style屬性,屬性中定義對應(yīng)的css樣式即可,比如style={{fontSize:'15px'}}
外層花括號是語法,內(nèi)層花括號是對象邊界符
14. Props校驗數(shù)據(jù)類型
array(數(shù)組)、bool(布爾值)、func(函數(shù)number(數(shù)字)、object(對象)、string(字符串)
15.受控組件和非受控組件
受控組件
由React控制的輸入表單元素而改變其值的方式,稱為受控組件。
比如,給表單元素input綁定一個onChange事件,當(dāng)input狀態(tài)發(fā)生變化時就會觸發(fā)onChange事件,從而更新組件的state。
非受控組件
非受控組件指的是,表單數(shù)據(jù)由DOM本身處理。即不受setState()的控制,與傳統(tǒng)的HTML表單輸入相似,input輸入值即顯示最新值。
在非受控組件中,可以使用一個ref來從DOM獲得表單值。
16. props和state的區(qū)別
props是指組件間傳遞的一種方式,props自然也可以傳遞state。由于React的數(shù)據(jù)流是自上而下的,所以是從父組件向子組件進行傳遞;另外組件內(nèi)部的this.props屬性是只讀的不可修改!
state是組件內(nèi)部的狀態(tài)(數(shù)據(jù)),不能夠直接修改,必須要通過setState來改變值的狀態(tài),從而達到更新組件內(nèi)部數(shù)據(jù)的作用。
17.組件傳值的方式?
父傳子組件通信, 子傳父組件通信 兄弟組件通信
18.父傳子通信流程
- 在父組件中的子組件標(biāo)簽上綁定自定義屬性,掛載傳遞的數(shù)據(jù)
- 子組件中props接受傳遞的數(shù)據(jù),直接使用即可
19. 子傳父通信的流程
- 父組件中子組件標(biāo)簽上綁定一個屬性,傳遞一個方法給子組件
- 子組件中通過props接受這個方法,直接調(diào)用,傳遞相應(yīng)的參數(shù)即可
20.非父子組件通信
- 狀態(tài)提升(中間人模式)
React中的狀態(tài)提升概括來說,就是將多個組件需要共享的狀態(tài)提升到它們最近的父組件,在父組件上改變這個狀態(tài)然后通過props分發(fā)給子組件 - context狀態(tài)樹傳參
21.context狀態(tài)樹是怎么運行的?
- 在父組件中我們通過createContext() 創(chuàng)建一個空對象,在父組件的最外層我們使用Provider包裹數(shù)據(jù),通過value綁定要傳遞的對象數(shù)據(jù)。
- 在嵌套的子組件中,我們有兩種方式獲取數(shù)據(jù):
(1) 我們可以使用Customer標(biāo)簽,在標(biāo)簽中綁定一個箭頭函數(shù),函數(shù)的形參context就是value傳遞的數(shù)據(jù)
(2). class組件中我們可以定義static contextType=context對象,組件中直接使用this.context獲取數(shù)據(jù)。
22.React生命周期分為幾個階段?
Mounting(掛載階段):已插入真實 DOM
Updating(更新階段):正在被重新渲染
Unmounting(卸載階段):已移出真實 DOM
23. 簡述React的生命周期函數(shù)?
掛載階段:
- constructor() 在 React 組件掛載之前,會調(diào)用它的構(gòu)造函數(shù)。
- componentWillMount: 在調(diào)用 render 方法之前調(diào)用,并且在初始掛載及后續(xù)更新時都會被調(diào)用。
- componentDidMount(): 在組件掛載后(插入 DOM 樹中)立即調(diào)用
更新運行階段:
- componentWillReceiveProps: 在接受父組件改變后的props需要重新渲染組件時用到的比較多,外部組件傳遞頻繁的時候會導(dǎo)致效率比較低
- shouldComponentUpdate():用于控制組件重新渲染的生命周期,state發(fā)生變化,組件會進入重新渲染的流程,在這里return false可以阻止組件的更新
- render(): render() 方法是 class 組件中唯一必須實現(xiàn)的方法。
- componentWillUpdate()*: shouldComponentUpdate返回true以后,組件進入重新渲染完成之前進入這個函數(shù)。
-
componentDidUpdate(): 每次state改變并重新渲染頁面后都會進入這個生命周期
卸載或銷毀階段
componentWillUnmount (): 在此處完成組件的卸載和數(shù)據(jù)的銷毀。
24. React舊生命周期有哪些問題?
(1) componentWillMount ,在ssr中 這個方法將會被多次調(diào)用, 所以會重復(fù)觸發(fā)多遍,同時在這里如果綁定事件,
將無法解綁,導(dǎo)致內(nèi)存泄漏 , 變得不夠安全高效逐步廢棄。
(2) componentWillReceiveProps 外部組件多次頻繁更新傳入多次不同的 props,會導(dǎo)致不必要的異步請求
(3) componetWillupdate, 更新前記錄 DOM 狀態(tài), 可能會做一些處理,與componentDidUpdate相隔時間如果過長, 會導(dǎo)致 狀態(tài)不太信
25. React新生命周期有哪些改變?
- 用 getDerivedStateFromProps替換了 compoentWillMount和compontWillReceiveProps生命周期函數(shù)
- 用getSnapshotBeforeUpdate函數(shù)替換componetWillUpdate方法,避免和CompoentDidUpdate函數(shù)中獲取數(shù)據(jù)不一致的問題
26. react的路由幾種模式,是什么?
兩種路由模式:
一種是Hash路由模式,用的是HashRouter組件
一種是歷史路由模式,用的是BrowserRouter組件綁定
27. react路由常用的組件有哪些?
HashRouter或BrowserRouter配置路由模式
Route 定義路由組件映射關(guān)系
Redirect 設(shè)置路由重定向
NavLink 或者Link 頁面路由跳轉(zhuǎn)
Switch 路由匹配,當(dāng)path匹配到一個component之后,將不會再想下繼續(xù)匹配,提高了程序效率
28. react路由傳參的方式有哪些?
//隱士參數(shù)傳遞
(1) this.props.history.push({ pathname : '/user' ,query : {id:100}})
this.props.location.query.id 獲取query傳遞的參數(shù)據(jù),刷新數(shù)據(jù)不在
(2) this.props.history.push({ pathname:'/user',state:{id: 1000 } }) this.props.location.state.id 獲取state的數(shù)據(jù),刷新數(shù)據(jù)還在
- url傳參方式 (<Link to="/user?id=100"></Link>) history.location.search獲取數(shù)據(jù)比較麻煩,得自己解析
- 動態(tài)路由定義
/detail/:id=>/detail/100=> location.match.params中接受的參數(shù)是 {id:100}
29. react路由跳轉(zhuǎn)的方式有哪些?
聲明式導(dǎo)航:
使用NavLink或者Link跳轉(zhuǎn), to屬性后面跟字符串或者跟對象
編程式導(dǎo)航跳轉(zhuǎn):
props.history.push(url) 跳轉(zhuǎn)頁面可以返回上一頁,保留歷史記錄
props.history.replace(url) 跳轉(zhuǎn)頁面,清空歷史記錄
props.history.go(num) 返回第幾個頁面
30. react路由嵌套如何配置?
- 配置父組件的路由地址,在父組件中配置子組件的路由映射關(guān)系
- 關(guān)閉父組件路由配置exact屬性,避免精準(zhǔn)匹配
- 父組件路由地址作為子組件路由地址的開始的一部分。比如父組件是/index 子組件應(yīng)該是/index/子組件地址
31. withRouter是干什么的?
不是所有組件都直接與路由相連(比如拆分的子組件)的,當(dāng)這些組件需要路由參數(shù)時,使用withRouter就可以給此組件傳入路由參數(shù),將react-router的history、location、match三個對象傳入props對象上,此時就可以使用this.props.history跳轉(zhuǎn)頁面了或者接受參數(shù)了
32. 什么是Redux?
在react中每個組件的state是由自身進行管理,包括組件定義自身的state、組件之間的通信通過props傳遞、使用Context實現(xiàn)數(shù)據(jù)共享等,如果讓每個組件都存儲自身相關(guān)的狀態(tài),理論上來講不會影響應(yīng)用的運行,但在開發(fā)及后期我們將比較難以維護,所以我們可以把數(shù)據(jù)進行集中式的管理,redux就是一個實現(xiàn)上述集中管理的容器的工具,
redux并不是只應(yīng)用在react中,還與其他界面庫一起使用,如Vue
33. Redux的三大原則
- state數(shù)據(jù)必須是單一數(shù)據(jù)源
- redux中的state數(shù)據(jù)必須 是只讀的,只能通過dispatch調(diào)用actions修改
- Reducer必須使用純函數(shù)來執(zhí)行修改
34. redux的執(zhí)行原理
React的組件需要獲取或者修改頁面的數(shù)據(jù),通過dispatch方法調(diào)用actions進入到Reducer函數(shù)中修改state的數(shù)據(jù)內(nèi)容,state更新后,通知組件更新頁面即可。
35. redux的使用步驟
- 創(chuàng)建一個store文件夾,新建一個index.js文件
- 文件中導(dǎo)入redux的createStore方法,用于創(chuàng)建公共數(shù)據(jù)區(qū)域
- 創(chuàng)建一個reducer純函數(shù),接受兩個參數(shù)state,actions分別表示分別表示數(shù)據(jù)和操作state的方法,返回state數(shù)據(jù)給組件頁面
- 把reducer作為createStore的參數(shù)拋出
- 在需要使用的頁面導(dǎo)入store文件,通過store.getState獲取數(shù)據(jù),通過store.dispatch觸發(fā)action修改state數(shù)據(jù)
- store.subscrible 方法監(jiān)聽 store 的改變,避免數(shù)據(jù)不更新
36. state和props有什么區(qū)別
相同點:
兩者都是 JavaScript 對象
兩者都是用于保存信息
props 和 state 都能觸發(fā)渲染更新
區(qū)別:
props 是外部傳遞給組件的,而 state 是在組件內(nèi)被組件自己管理的,一般在 constructor 中初始化
props 在組件內(nèi)部是不可修改的,但 state 在組件內(nèi)部可以進行修改
state 是多變的、可以修改
37. super() 和super(props)有什么區(qū)別?
在 React 中,類組件基于 ES6,所以在 constructor 中必須使用 super
在調(diào)用 super 過程,無論是否傳入 props,React 內(nèi)部都會將 porps 賦值給組件實例 porps 屬性中
如果只調(diào)用了 super(),那么 this.props 在 super() 和構(gòu)造函數(shù)結(jié)束之間仍是 undefined
38. 說說 React中的setState執(zhí)行機制
一個組件的顯示形態(tài)可以由數(shù)據(jù)狀態(tài)和外部參數(shù)所決定,而數(shù)據(jù)狀態(tài)就是state, 當(dāng)需要修改里面的值的狀態(tài)需要通過調(diào)用setState來改變,從而達到更新組件內(nèi)部數(shù)據(jù)的作用
setState第一個參數(shù)可以是一個對象,或者是一個函數(shù),而第二個參數(shù)是一個回調(diào)函數(shù),用于可以實時的獲取到更新之后的數(shù)據(jù)
在使用setState更新數(shù)據(jù)的時候,setState的更新類型分成:同步更新,異步更新
在組件生命周期或React合成事件中,setState是異步
在setTimeout或者原生dom事件中,setState是同步
對同一個值進行多次 setState, setState 的批量更新策略會對其進行覆蓋,取最后一次的執(zhí)行結(jié)果
39. React的事件機制總結(jié)
React事件機制總結(jié)如下:
- React 上注冊的事件最終會綁定在document這個 DOM 上,而不是 React 組件對應(yīng)的 DOM(減少內(nèi)存開銷就是因為所有的事件都綁定在 document 上,其他節(jié)點沒有綁定事件)
- React 自身實現(xiàn)了一套事件冒泡機制,所以這也就是為什么我們 event.stopPropagation()無效的原因。
- React 通過隊列的形式,從觸發(fā)的組件向父組件回溯,然后調(diào)用他們 JSX 中定義的 callback
- React 有一套自己的合成事件 SyntheticEvent
40. 說說對React refs 的理解?應(yīng)用場景?
創(chuàng)建ref的形式有三種:
- 傳入字符串,使用時通過 this.refs.傳入的字符串的格式獲取對應(yīng)的元素
- 傳入對象,對象是通過 React.createRef() 方式創(chuàng)建出來,使用時獲取到創(chuàng)建的對象中存在 current 屬性就是對應(yīng)的元素
- 傳入hook,hook是通過 useRef() 方式創(chuàng)建,使用時通過生成hook對象的 current 屬性就是對應(yīng)的元素
在某些情況下,我們會通過使用refs來更新組件,但這種方式并不推薦,更多情況我們是通過props與state的方式進行去重新渲染子元素
但下面的場景使用refs非常有用:
- 對Dom元素的焦點控制、內(nèi)容選擇、控制
- 對Dom元素的內(nèi)容設(shè)置及媒體播放
- 對Dom元素的操作和對組件實例的操作
- 集成第三方 DOM 庫
41. 說說對高階組件的理解?應(yīng)用場景?
高階函數(shù)(Higher-order function),至少滿足下列一個條件的函數(shù)
接受一個或多個函數(shù)作為輸入,輸出一個函數(shù)
在React中,高階組件即接受一個或多個組件作為參數(shù)并且返回一個組件,本質(zhì)也就是一個函數(shù),并不是一個組件
高階組件的主要功能是封裝并分離組件的通用邏輯,讓通用邏輯在組件間更好地被復(fù)用
高階組件可以傳遞所有的props,但是不能傳遞ref
高階組件能夠提高代碼的復(fù)用性和靈活性,在實際應(yīng)用中,常常用于與核心業(yè)務(wù)無關(guān)但又在多個模塊使用的功能,如權(quán)限控制、日志記錄、數(shù)據(jù)校驗、異常處理、統(tǒng)計上報等
42. 說說對Redux中間件的理解?常用的中間件有哪些?
Redux中,中間件就是放在就是在dispatch過程,在分發(fā)action進行攔截處理
前面我們了解到了Redux整個工作流程,當(dāng)action發(fā)出之后,reducer立即算出state,整個過程是一個同步的操作
那么如果需要支持異步操作,或者支持錯誤處理、日志監(jiān)控,這個過程就可以用上中間件,其本質(zhì)上一個函數(shù),對store.dispatch方法進行了改造,在發(fā)出 Action和執(zhí)行 Reducer這兩步之間,添加了其他功能
常用的redux中間件,如:
redux-thunk:用于異步操作
redux-logger:用于日志記錄
中間件都需要通過applyMiddlewares進行注冊,作用是將所有的中間件組成一個數(shù)組,依次執(zhí)行然后作為第二個參數(shù)傳入到createStore中
const store = createStore(
reducer,
applyMiddleware(thunk, logger)
);