React 常用面試題目與分析

分類:技術 時間:2017-01-13

React 常用面試題目與分析 從屬于筆者的 Web 前端入門與工程實踐 ,更多前端思考借鑒 2016-我的前端之路:工具化與工程化

調用 setState 之后發生了什么?

在代碼中調用 setState 函數之后,React 會將傳入的參數對象與組件當前的狀態合并,然后觸發所謂的調和過程(Reconciliation)。經過調和過程,React 會以相對高效的方式根據新的狀態構建 React 元素樹并且著手重新渲染整個UI界面。在 React 得到元素樹之后,React 會自動計算出新的樹與老樹的節點差異,然后根據差異對界面進行最小化重渲染。在差異計算算法中,React 能夠相對精確地知道哪些位置發生了改變以及應該如何改變,這就保證了按需更新,而不是全部重新渲染。

React 中 Element 與 Component 的區別是?

簡單而言,React Element 是描述屏幕上所見內容的數據結構,是對于 UI 的對象表述。典型的 React Element 就是利用 JSX 構建的聲明式代碼片然后被轉化為 createElement 的調用組合。而 React Component 則是可以接收參數輸入并且返回某個 React Element 的函數或者類。更多介紹可以參考 React Elements vs React Components

在什么情況下你會優先選擇使用 Class Component 而不是 Functional Component?

在組件需要包含內部狀態或者使用到生命周期函數的時候使用 Class Component ,否則使用函數式組件。

React 中 refs 的作用是什么?

Refs 是 React 提供給我們的安全訪問 DOM 元素或者某個組件實例的句柄。我們可以為元素添加 ref 屬性然后在回調函數中接受該元素在 DOM 樹中的句柄,該值會作為回調函數的第一個參數返回:

class CustomForm extends Component {
  handleSubmit = () =gt; {
    console.log(quot;Input Value: quot;, this.input.value)
  }
  render () {
    return (
      lt;form onSubmit={this.handleSubmit}gt;
        lt;input
          type='text'
          ref={(input) =gt; this.input = input} /gt;
        lt;button type='submit'gt;Submitlt;/buttongt;
      lt;/formgt;
    )
  }
}

上述代碼中的 input 域包含了一個 ref 屬性,該屬性聲明的回調函數會接收 input 對應的 DOM 元素,我們將其綁定到 this 指針以便在其他的類函數中使用。另外值得一提的是,refs 并不是類組件的專屬,函數式組件同樣能夠利用閉包暫存其值:

function CustomForm ({handleSubmit}) {
  let inputElement
  return (
    lt;form onSubmit={() =gt; handleSubmit(inputElement.value)}gt;
      lt;input
        type='text'
        ref={(input) =gt; inputElement = input} /gt;
      lt;button type='submit'gt;Submitlt;/buttongt;
    lt;/formgt;
  )
}

React 中 keys 的作用是什么?

Keys 是 React 用于追蹤哪些列表中元素被修改、被添加或者被移除的輔助標識。

render () {
  return (
    lt;ulgt;
      {this.state.todoItems.map(({task, uid}) =gt; {
        return lt;li key={uid}gt;{task}lt;/ligt;
      })}
    lt;/ulgt;
  )
}

在開發過程中,我們需要保證某個元素的 key 在其同級元素中具有唯一性。在 React Diff 算法中 React 會借助元素的 Key 值來判斷該元素是新近創建的還是被移動而來的元素,從而減少不必要的元素重渲染。此外,React 還需要借助 Key 值來判斷元素與本地狀態的關聯關系,因此我們絕不可忽視轉換函數中 Key 的重要性。

如果你創建了類似于下面的 Twitter 元素,那么它相關的類定義是啥樣子的?

lt;Twitter username='tylermcginnis33'gt;
  {(user) =gt; user === null
    ? lt;Loading /gt;
    : lt;Badge info={user} /gt;}
lt;/Twittergt;
import React, { Component, PropTypes } from 'react'
import fetchUser from 'twitter'
// fetchUser take in a username returns a promise
// which will resolve with that username's data.
class Twitter extends Component {
  // finish this
}

如果你還不熟悉回調渲染模式(Render Callback Pattern),這個代碼可能看起來有點怪。這種模式中,組件會接收某個函數作為其子組件,然后在渲染函數中以 props.children 進行調用:

import React, { Component, PropTypes } from 'react'
import fetchUser from 'twitter'
class Twitter extends Component {
  state = {
    user: null,
  }
  static propTypes = {
    username: PropTypes.string.isRequired,
  }
  componentDidMount () {
    fetchUser(this.props.username)
      .then((user) =gt; this.setState({user}))
  }
  render () {
    return this.props.children(this.state.user)
  }
}

這種模式的優勢在于將父組件與子組件解耦和,父組件可以直接訪問子組件的內部狀態而不需要再通過Props傳遞,這樣父組件能夠更為方便地控制子組件展示的UI界面。譬如產品經理讓我們將原本展示的 Badge 替換為 Profile ,我們可以輕易地修改下回調函數即可:

lt;Twitter username='tylermcginnis33'gt;
  {(user) =gt; user === null
    ? lt;Loading /gt;
    : lt;Profile info={user} /gt;}
lt;/Twittergt;

Controlled Component 與 Uncontrolled Component 之間的區別是什么?

React 的核心組成之一就是能夠維持內部狀態的自治組件,不過當我們引入原生的HTML表單元素時(input,select,textarea 等),我們是否應該將所有的數據托管到 React 組件中還是將其仍然保留在 DOM 元素中呢?這個問題的答案就是受控組件與非受控組件的定義分割。受控組件(Controlled Component)代指那些交由 React 控制并且所有的表單數據統一存放的組件。譬如下面這段代碼中 username 變量值并沒有存放到DOM元素中,而是存放在組件狀態數據中。任何時候我們需要改變 username 變量值時,我們應當調用 setState 函數進行修改。

class ControlledForm extends Component {
  state = {
    username: ''
  }
  updateUsername = (e) =gt; {
    this.setState({
      username: e.target.value,
    })
  }
  handleSubmit = () =gt; {}
  render () {
    return (
      lt;form onSubmit={this.handleSubmit}gt;
        lt;input
          type='text'
          value=http://www.tuicool.com/articles/{this.state.username}
          onChange={this.updateUsername} /gt;
        lt;button type='submit'gt;Submitlt;/buttongt;
      lt;/formgt;
    )
  }
}

而非受控組件(Uncontrolled Component)則是由DOM存放表單數據,并非存放在 React 組件中。我們可以使用 refs 來操控DOM元素:

class UnControlledForm extends Component {
  handleSubmit = () =gt; {
    console.log(quot;Input Value: quot;, this.input.value)
  }
  render () {
    return (
      lt;form onSubmit={this.handleSubmit}gt;
        lt;input
          type='text'
          ref={(input) =gt; this.input = input} /gt;
        lt;button type='submit'gt;Submitlt;/buttongt;
      lt;/formgt;
    )
  }
}

竟然非受控組件看上去更好實現,我們可以直接從 DOM 中抓取數據,而不需要添加額外的代碼。不過實際開發中我們并不提倡使用非受控組件,因為實際情況下我們需要更多的考慮表單驗證、選擇性的開啟或者關閉按鈕點擊、強制輸入格式等功能支持,而此時我們將數據托管到 React 中有助于我們更好地以聲明式的方式完成這些功能。引入 React 或者其他 MVVM 框架最初的原因就是為了將我們從繁重的直接操作 DOM 中解放出來。

在生命周期中的哪一步你應該發起 AJAX 請求?

我們應當將AJAX 請求放到 componentDidMount 函數中執行,主要原因有下:

  • React 下一代調和算法 Fiber 會通過開始或停止渲染的方式優化應用性能,其會影響到 componentWillMount 的觸發次數。對于 componentWillMount 這個生命周期函數的調用次數會變得不確定,React 可能會多次頻繁調用 componentWillMount。如果我們將 AJAX 請求放到 componentWillMount 函數中,那么顯而易見其會被觸發多次,自然也就不是好的選擇。

  • 如果我們將 AJAX 請求放置在生命周期的其他函數中,我們并不能保證請求僅在組件掛載完畢后才會要求響應。如果我們的數據請求在組件掛載之前就完成,并且調用了 setState 函數將數據添加到組件狀態中,對于未掛載的組件則會報錯。而在 componentDidMount 函數中進行 AJAX 請求則能有效避免這個問題。

shouldComponentUpdate 的作用是啥以及為何它這么重要?

shouldComponentUpdate 允許我們手動地判斷是否要進行組件更新,根據組件的應用場景設置函數的合理返回值能夠幫我們避免不必要的更新。

如何告訴 React 它應該編譯生產環境版本?

通常情況下我們會使用 Webpack 的 DefinePlugin 方法來將 NODE_ENV 變量值設置為 production。編譯版本中 React 會忽略 propType 驗證以及其他的告警信息,同時還會降低代碼庫的大小,React 使用了 Uglify 插件來移除生產環境下不必要的注釋等信息。

為什么我們需要使用 React 提供的 Children API 而不是 JavaScript 的 map?

props.children 并不一定是數組類型,譬如下面這個元素:

lt;Parentgt;
  lt;h1gt;Welcome.lt;/h1gt;
lt;/Parentgt;

如果我們使用 props.children.map 函數來遍歷時會受到異常提示,因為在這種情況下 props.children 是對象(object)而不是數組(array)。React 當且僅當超過一個子元素的情況下會將 props.children 設置為數組,就像下面這個代碼片:

lt;Parentgt;
  lt;h1gt;Welcome.lt;/h1gt;
  lt;h2gt;props.children will now be an arraylt;/h2gt;
lt;/Parentgt;

這也就是我們優先選擇使用 React.Children.map 函數的原因,其已經將 props.children 不同類型的情況考慮在內了。

概述下 React 中的事件處理邏輯

為了解決跨瀏覽器兼容性問題,React 會將瀏覽器原生事件(Browser Native Event)封裝為合成事件(SyntheticEvent)傳入設置的事件處理器中。這里的合成事件提供了與原生事件相同的接口,不過它們屏蔽了底層瀏覽器的細節差異,保證了行為的一致性。另外有意思的是,React 并沒有直接將事件附著到子元素上,而是以單一事件監聽器的方式將所有的事件發送到頂層進行處理。這樣 React 在更新 DOM 的時候就不需要考慮如何去處理附著在 DOM 上的事件監聽器,最終達到優化性能的目的。

createElement 與 cloneElement 的區別是什么?

createElement 函數是 JSX 編譯之后使用的創建 React Element 的函數,而 cloneElement 則是用于復制某個元素并傳入新的 Props。

傳入 setState 函數的第二個參數的作用是什么?

該函數會在 setState 函數調用完成并且組件開始重渲染的時候被調用,我們可以用該函數來監聽渲染是否完成:

this.setState(
  { username: 'tylermcginnis33' },
  () =gt; console.log('setState has finished and the component has re-rendered.')
)

下述代碼有錯嗎?

this.setState((prevState, props) =gt; {
  return {
    streak: prevState.streak   props.count
  }
})

這段代碼沒啥問題,不過只是不太常用罷了,詳細可以參考 React中setState同步更新策略


Tags: React

文章來源:https://segmentfault.com/a/1190000008102870


ads
ads

相關文章
ads

相關文章

ad