天天看點

【前端面試題】—30道常見React基礎面試題(附答案)

【前端面試題】—30道常見React基礎面試題(附答案)

當今最流行的架構非 React莫屬。 React以其岀色的性能,颠覆了網際網路的理念,簡單的開發方式受到許多開發者的青睐。

是以,在 React中,虛拟DOM、元件的生命周期、元件的通信、元件的限制性,配合 Reflux、 Redux等架構的使用,基于 EMAScript6文法開發,以及 Webpack編譯等都是讀者要掌握的内容。

當然, React的三大特色(虛拟DOM、元件開發、多端适配)的具體實作,開發者也要有所了解。

1、當調用 setState的時候,發生了什麼操作?

當調用 setState時, React做的第一件事是将傳遞給setState的對象合并到元件的目前狀态,這将啟動一個稱為和解( reconciliation)的過程。

和解的最終目标是,根據這個新的狀态以最有效的方式更新DOM。

為此, React将建構一個新的 React虛拟DOM樹(可以将其視為頁面DOM元素的對象表示方式)。

一旦有了這個DOM樹,為了弄清DOM是如何響應新的狀态而改變的, React會将這個新樹與上一個虛拟DOM樹比較。

這樣做, React會知道發生的确切變化,并且通過了解發生的變化後,在絕對必要的情況下進行更新DOM,即可将因操作DOM而占用的空間最小化。

2、在 React中元素( element)群組件( component)有什麼差別?

簡單地說,在 React中元素(虛拟DOM)描述了你在螢幕上看到的DOM元素。

換個說法就是,在 React中元素是頁面中DOM元素的對象表示方式。在 React中元件是一個函數或一個類,它可以接受輸入并傳回一個元素。

注意:工作中,為了提高開發效率,通常使用JSX文法表示 React元素(虛拟DOM)。在編譯的時候,把它轉化成一個 React. createElement調用方法。

3、什麼時候使用類元件( Class Component)?什麼時候使用功能元件(Functional Component)?

如果元件具有狀态( state)或生命周期方法,請使用類元件;否則,使用功能元件。

4、什麼是 React的refs?為什麼它們很重要?

refs允許你直接通路DOM元素或元件執行個體。為了使用它們,可以向元件添加個ref屬性。

如果該屬性的值是一個回調函數,它将接受底層的DOM元素或元件的已挂載執行個體作為其第一個參數。可以在元件中存儲它。

export class App extends Component  {
showResult ( ) {
console. log(this. input. value)
}
render ( ) {
return (
<div>
<input  type="text" ref={input => this .input =input } />
< button onClick={this. showResult.bind(this)}>展示結果</ button>
</div>
);
}
}      

如果該屬性值是一個字元串, React将會在元件執行個體化對象的refs屬性中,存儲一個同名屬性,該屬性是對這個DOM元素的引用。可以通過原生的 DOM API操作它。

export class App extends Component {
 showResult( )
console .log ( this.refs.username.value)
render ( ){
  return (
<div>
<input type="text" ref="username"/>
< button onClick={this. showResu1t.bind (this)}>展示結果</ button>
</div>
);
}
}      

5、React 中的key是什麼?為什麼它們很重要?

key可以幫助 React跟蹤循環建立清單中的虛拟DOM元素,了解哪些元素已更改、添加或删除。

每個綁定key的虛拟DOM元素,在兄弟元素之間都是獨一無二的。在 React的和解過程中,比較新的虛拟DOM樹與上一個虛拟DOM樹之間的差異,并映射到頁面中。key使 React處理清單中虛拟DOM時更加高效,因為 React可以使用虛拟DOM上的key屬性,快速了解元素是新的、需要删除的,還是修改過的。如果沒有key,Rεat就不知道清單中虛拟DOM元素與頁面中的哪個元素相對應。是以在建立清單的時候,不要忽略key。

6、如果建立了類似于下面的 Icketang元素,那麼該如何實作 Icketang類?

< Icketang username="雨夜清荷">
{user = > user ?<Info user={user} />:<Loading />}
</Icketang>
import React, { Component } fromr "react";
 export class Icketang extends Component {
//請實作你的代碼
}      

在上面的案例中,一個元件接受一個函數作為它的子元件。Icketang元件的子元件是一個函數,而不是一個常用的元件。這意味着在實作 Icketang元件時,需要将props. children作為一個函數來處理。

具體實作如下。

import React,  { Component } from "react";
class Icketang extends Component {
 constructor ( props ){
  super ( props ) 
this .state = {
 user : props.user 
}
}
componentDidMount( ) {
//模拟異步擷取資料操作,更新狀态
setTimeout ( ( ) => this .setstate ({
user:'有課前端網'
}),2000)
}
render ( ) {
return this.props.children ( this .state.user )
}
}
class Loading extends Component {
 render  ( ) {
 return <p>Loading.</p>
}
}
class Info extends Component  { 
render ( ) {
 return <hl> { this .props.user }</h1>
}
}      

調用 Icketang元件,并傳遞給user屬性資料,把 props.children作為一個函數來處理。這種模式的好處是,我們已經将父元件與子元件分離了,父元件管理狀态。父元件的使用者可以決定父元件以何種形式渲染子元件。

為了示範這一點,在渲染 Icketang元件時,分别傳遞和不傳遞user屬性資料來觀察渲染結果。

import {render} from  "react-dom";
 render (<Icketang>
{ user = > user ?<Info user = {user} /> :<Loading /> }
</Icketang> , ickt)      

上述代碼沒有為 Icketang元件傳遞user屬性資料,是以将首先渲染 Loading元件,當父元件的user狀态資料發生改變時,我們發現Info元件可以成功地渲染出來。

render(< Icketang user="雨夜清荷">
{ user => user ?<Info user = {user} /> :<Loading />}
</Icketang>, ickt)      

上述代碼為 Icketang元件傳遞了user屬性資料,是以将直接渲染Info元件,當父元件的user狀态資料發生改變時,我們發現Info元件産生了更新,在整個過程中, Loading元件都未渲染。

7、限制性元件( controlled component)與非限制性元件( uncontrolled  component)有什麼差別?

在 React中,元件負責控制和管理自己的狀态。

如果将HTML中的表單元素( input、 select、 textarea等)添加到元件中,當使用者與表單發生互動時,就涉及表單資料存儲問題。根據表單資料的存儲位置,将元件分成約東性元件和非約東性元件。

限制性元件( controlled component)就是由 React控制的元件,也就是說,表單元素的資料存儲在元件内部的狀态中,表單到底呈現什麼由元件決定。

如下所示, username沒有存儲在DOM元素内,而是存儲在元件的狀态中。每次要更新 username時,就要調用 setState更新狀态;每次要擷取 username的值,就要擷取元件狀态值。

class App extends Component {
//初始化狀态
constructor ( props ) {
 super ( props )
this .state = {
 username:'有課前端網'
}
}
//檢視結果
showResult ( ) {
//擷取資料就是擷取狀态值
console. log ( this .state. username )
}
changeUsername (e) {
//原生方法擷取
var value =e .target .value
//更新前,可以進行髒值檢測
//更新狀态
this .setState ( {t
username:value
} )
}
//渲染元件
render( ) {
//傳回虛拟DOM 
return (
<div>
<p>
{/*輸入框綁定va1ue*/}
<input type="text" onChange={ this.changeUsername .bind (this ) }
value= { this .state.username }/>
</p>
<p>
< button onClick={this.showResult.bind (this)}>檢視結果</ button>
</p>
</div>
)
}
}      

非限制性元件( uncontrolled component)就是指表單元素的資料交由元素自身存儲并處理,而不是通過 React元件。表單如何呈現由表單元素自身決定。

如下所示,表單的值并沒有存儲在元件的狀态中,而是存儲在表單元素中,當要修改表單資料時,直接輸入表單即可。有時也可以擷取元素,再手動修改它的值。當要擷取表單資料時,要首先擷取表單元素,然後通過表單元素擷取元素的值。

注意:為了友善在元件中擷取表單元素,通常為元素設定ref屬性,在元件内部通過refs屬性擷取對應的DOM元素。

class App extends Component {
//檢視結果
showResult ( ) {
//擷取值
console. log(this. refs. username .value)
//修改值,就是修改元素自身的值
this.refs.username.value="專業前端學習平台"
//渲染元件
render ( ) {
//傳回虛拟DOM 
return (
<div>
<p>
{/*非限制性元件中,表單元素通過 defaultvalue定義*/}
< input type="text"  ref=" username"  defaultvalue="有課前端網"/>
</p>
<p>
< button onClick={this. showResult.bind ( this ) }>檢視結果</button>
</p>
</div>
 )
  }
   }      

雖然非約東性元件通常更容易實作,可以通過refs直接擷取DOM元素,并擷取其值,但是 React建議使用限制性元件。主要原因是,約東性元件支援即時字段驗證,允許有條件地禁用/啟用按鈕,強制輸入格式等。

8、在哪個生命周期中你會發出Ajax請求?為什麼?

Ajax請求應該寫在元件建立期的第五個階段,即 componentDidMount生命周期方法中。原因如下。

在建立期的其他階段,元件尚未渲染完成。而在存在期的5個階段,又不能確定生命周期方法一定會執行(如通過 shouldComponentUpdate方法優化更新等)。在銷毀期,元件即将被銷毀,請求資料變得無意義。是以在這些階段發岀Ajax請求顯然不是最好的選擇。

在元件尚未挂載之前,Ajax請求将無法執行完畢,如果此時送出請求,将意味着在元件挂載之前更新狀态(如執行 setState),這通常是不起作用的。

在 componentDidMount方法中,執行Ajax即可保證元件已經挂載,并且能夠正常更新元件。

9、shouldComponentUpdate有什麼用?為什麼它很重要?

元件狀态資料或者屬性資料發生更新的時候,元件會進入存在期,視圖會渲染更新。在生命周期方法 should ComponentUpdate中,允許選擇退出某些元件(和它們的子元件)的和解過程。

和解的最終目标是根據新的狀态,以最有效的方式更新使用者界面。如果我們知道使用者界面的某一部分不會改變,那麼沒有理由讓 React弄清楚它是否應該更新渲染。通過在 shouldComponentUpdate方法中傳回 false, React将讓目前元件及其所有子元件保持與目前元件狀态相同。

10、如何用 React建構( build)生産模式?

通常,使用 Webpack的 DefinePlugin方法将 NODE ENV設定為 production。這将剝離 propType驗證和額外的警告。除此之外,還可以減少代碼,因為 React使用 Uglify的dead-code來消除開發代碼和注釋,這将大大減少包占用的空間。

11、為什麼要使用 React. Children. map( props. children,( )=>)而不是props. children. map ( (  ) => )?

因為不能保證 props. children将是一個數組。

以下面的代碼為例。

<Parent>
<h1>有課前端網</h1>
</Parent>      

在父元件内部,如果嘗試使用 props.children. map映射子對象,則會抛出錯誤,因為props. children是一個對象,而不是一個數組。

如果有多個子元素, React會使 props.children成為一個數組,如下所示。

<Parent>
<h1>有課前端網</h1>
<h2>前端技術學習平台</h2>
</Parent>
不建議使用如下方式,在這個案例中會抛出錯誤。
class Parent extends Component {
 render ( ) {
 return (
<div> { this .props.children.map (obj = > obj ) }</div>
)
} 
}      

建議使用如下方式,避免在上一個案例中抛出錯誤。

class Parent extends Component  {
render ( ) {
  return (
<div> { React.Children.map ( this .props.children, obj => obj) }</div>
)
}
}      

12、描述事件在 React中的處理方式。

為了解決跨浏覽器相容性問題, React中的事件處理程式将傳遞 SyntheticEvent的執行個體,它是跨浏覽器事件的包裝器。這些 SyntheticEvent與你習慣的原生事件具有相同的接口,它們在所有浏覽器中都相容。

React實際上并沒有将事件附加到子節點本身。而是通過事件委托模式,使用單個事件監聽器監聽頂層的所有事件。這對于性能是有好處的。這也意味着在更新DOM時, React不需要擔心跟蹤事件監聽器。

13、createElement和 cloneElement有什麼差別?

createElement是JSX被轉載得到的,在 React中用來建立 React元素(即虛拟DOM)的内容。cloneElement用于複制元素并傳遞新的 props。

14、setState方法的第二個參數有什麼用?使用它的目的是什麼?

它是一個回調函數,當 setState方法執行結束并重新渲染該元件時調用它。在工作中,更好的方式是使用 React元件生命周期之——“存在期”的生命周期方法,而不是依賴這個回調函數。

export class App extends Component {
constructor (props) {
 super ( props )
this.state = {
username:"雨夜清荷"
}
}
render ( ) {
return (
<div> { this .state. username) </div>
);
}
componentDidMount ( ) {
this .setstate ( { 
 username :'有課前端網'
},( ) => console. log ( 're-rendered success. ' ) )      

15、這段代碼有什麼問題?

class App extends Component {
 constructor ( props )  {
super ( props )
this .state = {
username:"有課前端網", 
msg:' '
}
}
render ( ) {
return (
<div> { this .state. msg }</div>
);
}
componentDidMount ( )  {
 this .setState ( ( oldState, props ) => {
return {
msg:oldState .username + ' - ' + props.intro 
}
} )
}      

render ( < App intro=" 前端技術專業學習平台"></App>,ickt )

在頁面中正常輸出“有課前端網-前端技術專業學習平台”。但是這種寫法很少使用,并不是常用的寫法。React允許對 setState方法傳遞一個函數,它接收到先前的狀态和屬性資料并傳回一個需要修改的狀态對象,正如我們在上面所做的那樣。它不但沒有問題,而且如果根據以前的狀态( state)以及屬性來修改目前狀态,推薦使用這種寫法。

16、請說岀 React從 EMAScript5程式設計規範到 EMAScript6程式設計規範過程中的幾點改變。

主要改變如下。

(1)建立元件的方法不同。

EMAScript5版本中,定義元件用 React.createClass。EMAScript6版本中,定義元件要定義元件類,并繼承 Component類。

(2)定義預設屬性的方法不同。

EMAScript5版本中,用 getDefaultProps定義預設屬性。EMAScript6版本中,為元件定義 defaultProps靜态屬性,來定義預設屬性。

(3)定義初始化狀态的方法不同。EMAScript5版本中,用 getInitialState定義初始化狀态。EMAScript6版本中,在構造函數中,通過this. state定義初始化狀态。

注意:構造函數的第一個參數是屬性資料,一定要用 super繼承。

(4)定義屬性限制的方法不同。

EMAScript5版本中,用 propTypes定義屬性的限制。

EMAScript6版本中,為元件定義 propsTypes靜态屬性,來對屬性進行限制。

(5)使用混合對象、混合類的方法不同。

EMAScript5版本中,通過mixins繼承混合對象的方法。

EMAScript6版本中,定義混合類,讓混合類繼承 Component類,然後讓元件類繼承混合類,實作對混合類方法的繼承。

(6)綁定事件的方法不同。

EMAScript5版本中,綁定的事件回調函數作用域是元件執行個體化對象。

EMAScript6版本中,綁定的事件回調函數作用域是null。

(7)父元件傳遞方法的作用域不同。

EMAScript5版本中,作用域是父元件。 EMAScript6版本中,變成了null。

(8)元件方法作用域的修改方法不同。

EMAScript5版本中,無法改變作用域。

EMAScript6版本中,作用域是可以改變的。

17、React中D算法的原理是什麼?

原理如下。

(1)節點之間的比較。

節點包括兩種類型:一種是 React元件,另一種是HTML的DOM。

如果節點類型不同,按以下方式比較。

如果 HTML DOM不同,直接使用新的替換舊的。如果元件類型不同,也直接使用新的替換舊的。

如果 HTML DOM類型相同,按以下方式比較。

在 React裡樣式并不是一個純粹的字元串,而是一個對象,這樣在樣式發生改變時,隻需要改變替換變化以後的樣式。修改完目前節點之後,遞歸處理該節點的子節點。

如果元件類型相同,按以下方式比較。

如果元件類型相同,使用 React機制處理。一般使用新的 props替換舊的 props,并在之後調用元件的 componentWillReceiveProps方法,之前元件的 render方法會被調用。

節點的比較機制開始遞歸作用于它的子節點。

(2)兩個清單之間的比較。

一個節點清單中的一個節點發生改變, React無法很妤地處理這個問題。循環新舊兩個清單,并找出不同,這是 React唯一的處理方法。

但是,有一個辦法可以把這個算法的複雜度降低。那就是在生成一個節點清單時給每個節點上添加一個key。這個key隻需要在這一個節點清單中唯一,不需要全局唯一。

(3)取舍

需要注意的是,上面的啟發式算法基于兩點假設。

類型相近的節點總是生成同樣的樹,而類型不同的節點也總是生成不同的樹

可以為多次 render都表現穩定的節點設定key。

上面的節點之間的比較算法基本上就是基于這兩個假設而實作的。要提高 React應用的效率,需要按照這兩點假設來開發。

18、概述一下 React中的事件處理邏輯。

為了解決跨浏覽器相容性問題, React會将浏覽器原生事件( Browser Native Event)封裝為合成事件( Synthetic Event)并傳入設定的事件處理程式中。

這裡的合成事件提供了與原生事件相同的接口,不過它們屏蔽了底層浏覽器的細節差異,保證了行為的一緻性。另外, React并沒有直接将事件附着到子元素上,而是以單一事件監聽器的方式将所有的事件發送到頂層進行處理(基于事件委托原理)。

這樣 React在更新DOM時就不需要考慮如何處理附着在DOM上的事件監聽器,最終達到優化性能的目的。

19、傳入 setstate函數的第二個參數的作用是什麼?

第二個參數是一個函數,該函數會在 setState函數調用完成并且元件開始重渲染時調用,可以用該函數來監聽渲染是否完成。

this .setstate ({ 
username:'有課前端網'
}, ( ) => console.log ( 're-rendered success. ' ) )      

20、React和vue.js的相似性和差異性是什麼?

相似性如下。

(1)都是用于建立UI的 JavaScript庫。

(2)都是快速和輕量級的代碼庫(這裡指 React核心庫)。

(3)都有基于元件的架構。

(4)都使用虛拟DOM。

(5)都可以放在單獨的HTML檔案中,或者放在 Webpack設定的一個更複雜的子產品中。

(6)都有獨立但常用的路由器和狀态管理庫。

它們最大的差別在于 Vue. js通常使用HTML模闆檔案,而 React完全使用 JavaScript建立虛拟DOM。 Vue. js還具有對于“可變狀态”的“ reactivity”的重新渲染的自動化檢測系統。

21、生命周期調用方法的順序是什麼?

React生命周期分為三大周期,11個階段,生命周期方法調用順序分别如下。

(1)在建立期的五大階段,調用方法的順序如下。

  • getDetaultProps:定義預設屬性資料。
  • getInitialState:初始化預設狀态資料。
  • component WillMount:元件即将被建構。
  • render:渲染元件。
  • componentDidMount:元件建構完成

(2)在存在期的五大階段,調用方法的順序如下。

  • componentWillReceiveProps:元件即将接收新的屬性資料。
  • shouldComponentUpdate:判斷元件是否應該更新。
  • componnent WillUpdate:元件即将更新。
  • render:渲染元件。
  • componentDidUpdate:元件更新完成。

(3)在銷毀期的一個階段,調用方法 componentWillUnmount,表示元件即将被銷毀。

22、使用狀态要注意哪些事情?

要注意以下幾點。

  • 不要直接更新狀态
  • 狀态更新可能是異步的
  • 狀态更新要合并。
  • 資料從上向下流動

23、說說 React元件開發中關于作用域的常見問題。

在 EMAScript5文法規範中,關于作用域的常見問題如下。

(1)在map等方法的回調函數中,要綁定作用域this(通過bind方法)。

(2)父元件傳遞給子元件方法的作用域是父元件執行個體化對象,無法改變。

(3)元件事件回調函數方法的作用域是元件執行個體化對象(綁定父元件提供的方法就是父元件執行個體化對象),無法改變。

在 EMAScript6文法規範中,關于作用域的常見問題如下。

(1)當使用箭頭函數作為map等方法的回調函數時,箭頭函數的作用域是目前元件的執行個體化對象(即箭頭函數的作用域是定義時的作用域),無須綁定作用域。

(2)事件回調函數要綁定元件作用域。

(3)父元件傳遞方法要綁定父元件作用域。

總之,在 EMAScript6文法規範中,元件方法的作用域是可以改變的。

24、在 Redux中使用 Action要注意哪些問題?

在Redux中使用 Action的時候, Action檔案裡盡量保持 Action檔案的純淨,傳入什麼資料就傳回什麼資料,最妤把請求的資料和 Action方法分離開,以保持 Action的純淨。

25、在 Reducer檔案裡,對于傳回的結果,要注意哪些問題?

在 Reducer檔案裡,對于傳回的結果,必須要使用 Object.assign ( )來複制一份新的 state,否則頁面不會跟着資料重新整理。

return Object. assign ( { }, state, {
type:action .type,
shouldNotPaint : true
})      

26、如何使用4.0版本的 React Router?

React Router 4.0版本中對 hashHistory做了遷移,執行包安裝指令 npm install react-router-dom後,按照如下代碼進行使用即可。

import  { HashRouter, Route, Redirect, Switch  } from " react-router-dom"; 
class App extends Component {
render ( ) {
return (
<div>
<Switch>
<Route path="/list"  componen t= { List }></Route>
<Route path="/detail/:id" component= { Detail } > </Route>


<Redirect from="/ "  to="/list"> </Redirect>


</Switch>


</div>


)


}


}


const routes = (


<HashRouter>


<App> </App>


</HashRouter>


)


render(routes, ickt);      

27、在 ReactNative中,如何解決8081端口号被占用而提示無法通路的問題?

在運作 react-native start時添加參數port 8082;在 package.json中修改“scripts”中的參數,添加端口号;修改項目下的 node_modules \react-native\local- cli\server\server.js檔案配置中的 default端口值。

28、在 ReactNative中,如何解決 adb devices找不到連接配接裝置的問題?

在使用 Genymotion時,首先需要在SDK的 platform-tools中加入環境變量,然後在 Genymotion中單擊 Setting,選擇ADB頁籤,單擊 Use custom Android SDK tools,浏覽本地SDK的位置,單擊OK按鈕就可以了。啟動虛拟機後,在cmd中輸入 adb devices可以檢視裝置。

29、React- Router有幾種形式?

有以下幾種形式。

HashRouter,通過散列實作,路由要帶#。

BrowerRouter,利用HTML5中 history API實作,需要伺服器端支援,相容性不是很好。

30、在使用 React Router時,如何擷取目前頁面的路由或浏覽器中位址欄中的位址?

在目前元件的 props中,包含 location屬性對象,包含目前頁面路由位址資訊,在 match中存儲目前路由的參數等資料資訊。可以直接通過 this .props使用它們。

【前端面試題】—30道常見React基礎面試題(附答案)