React、React Native面試題


1.React Native相對於原生的ios和Android有哪些優勢。

react native一套代碼可以開發出跨平台app, 減少了人力、節省了時間、避免了 iOS 與 Android 版本發布的時間差,開發新功能可以更迅速。等等

2.React Native的優點和缺點在哪里。

缺點:內存、轉化為原生的

3.父傳子,子傳父數據傳遞方式。

props state refs 方面回答

4.如何實現底部TabBar的高度不一樣呢。(類似新浪微博底部加號)

主要考察flex布局絕對定位問題

5.請您簡單介紹一下redux、mobx。

redux ==> action/reducer/store
mobx ==>數據雙向綁定

6.當你調用setState的時候,發生了什么事。

當調用 setState 時,React會做的第一件事情是將傳遞給 setState 的對象合並到組件的當前狀態。 這將啟動一個稱為和解(reconciliation)的過程。 和解(reconciliation)的最終目標是以最有效的方式,根據這個新的狀態來更新UI。 為此,React將構建一個新的 React 元素樹(您可以將其視為 UI 的對象表示)。 一旦有了這個樹,為了弄清 UI 如何響應新的狀態而改變,React 會將這個新樹與上一個元素樹相比較( diff )。 通過這樣做, React 將會知道發生的確切變化,並且通過了解發生什么變化,只需在絕對必要的情況下進行更新即可最小化 UI 的占用空間。

7.React中Element 和 Component 有何區別。

簡單地說,一個 React element 描述了你想在屏幕上看到什么。 換個說法就是,一個 React element 是一些 UI 的對象表示。 一個 React Component 是一個函數或一個類, 它可以接受輸入並返回一個 React element (通常是通過 JSX ,它被轉化成一個 createElement 調用)。

8.shouldComponentUpdate 應該做什么

其實這個問題也是跟reconciliation有關系。 “和解( reconciliation )的最終目標是以最有效的方式,根據新的狀態更新用戶界面”。 如果我們知道我們的用戶界面(UI)的某一部分不會改變, 那么沒有理由讓 React 很麻煩地試圖去弄清楚它是否應該渲染。 通過從 shouldComponentUpdate 返回 false, React 將假定當前組件及其所有子組件將保持與當前組件相同

9.描述事件在React中的處理方式

為了解決跨瀏覽器兼容性問題, 您的 React 中的事件處理程序將傳遞 SyntheticEvent 的實例, 它是 React 的瀏覽器本機事件的跨瀏覽器包裝器。 這些 SyntheticEvent 與您習慣的原生事件具有相同的接口,除了它們在所有瀏覽器中都兼容。 有趣的是,React 實際上並沒有將事件附加到子節點本身。 React 將使用單個事件監聽器監聽頂層的所有事件。 這對於性能是有好處的,這也意味着在更新DOM時,React 不需要擔心跟蹤事件監聽器

10.reactJS的props.children.map函數來遍歷會收到異常提示,為什么。應該如何遍歷。

this.props.children 的值有三種可能

  1. 當前組件沒有子節點,它就是 undefined;
  2. 有一個子節點,數據類型是 object;
  3. 有多個子節點,數據類型就是 array.

系統提供React.Children.map()方法安全的遍歷子節點對象

11.XSS與CSRF介紹

XSS是一種跨站腳本攻擊,是屬於代碼注入的一種,攻擊者通過將代碼注入網頁中,其他用戶看到會受到影響(代碼內容有請求外部服務器);
CSRF是一種跨站請求偽造,冒充用戶發起請求,完成一些違背用戶請求的行為(刪帖,改密碼,發郵件,發帖等)

12.在使用redux過程中,如何防止定義的action-type的常量重復。

ES6引入了一種新的原始數據類型Symbol,表示獨一無二的值。 Symbol函數前不能使用new命令,否則會報錯。這是因為生成的Symbol是一個原始類型的值,不是對象 Symbol函數可以接受一個字符串作為參數,表示對Symbol實例的描述,主要是為了在控制台顯示,或者轉為字符串時,比較容易區分。

13 PureComponent 為什么比Component高效?

追查:這兩個組件的實現在ReactBaseClasses.js中間,除掉注釋后如下

function ReactComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

ReactComponent.prototype.isReactComponent = {};

ReactComponent.prototype.setState = function (partialState, callback) {
  !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : _prodInvariant('85') : void 0;
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

ReactComponent.prototype.forceUpdate = function (callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};


function ReactPureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

function ComponentDummy() {}
ComponentDummy.prototype = ReactComponent.prototype;

ReactPureComponent.prototype = new ComponentDummy();
ReactPureComponent.prototype.constructor = ReactPureComponent;
_assign(ReactPureComponent.prototype, ReactComponent.prototype);
ReactPureComponent.prototype.isPureReactComponent = true;

module.exports = {
  Component: ReactComponent,
  PureComponent: ReactPureComponent
};

發現Component就只實現了構造方法,定義了setState方法就完了。而ReactPureComponent更狠,就只是用js原型模擬繼承的方法繼承了Component,然后定義屬性isPureReactComponent為true。全局搜索isPureReactComponent屬性,發現在ReactCompositeComponent.js中有使用,這個類就是管理組件的更新、加載等的。關鍵代碼在updateComponent方法中,如下

var shouldUpdate = true;    // 這個變量決定是否需要重新渲染組件
  if (!this._pendingForceUpdate) {
    var prevState = inst.state;
    shouldUpdate = willReceive || nextState !== prevState;
    // inst代表組件實例,這個判斷條件就是組件是否自己實現了shouldComponentUpdate方法
    if (inst.shouldComponentUpdate) {
      if (__DEV__) {
        shouldUpdate = measureLifeCyclePerf(
          () => inst.shouldComponentUpdate(nextProps, nextState, nextContext),
          this._debugID,
          'shouldComponentUpdate',
        );
      } else {
        shouldUpdate = inst.shouldComponentUpdate(
          nextProps,
          nextState,
          nextContext,
        );
      }
    } else {// 組件沒有實現shouldComponentUpdate方法,且是PureComponent,采用shallowEqual淺比較
      if (this._compositeType === ReactCompositeComponentTypes.PureClass) {
        shouldUpdate = !shallowEqual(prevProps, nextProps) ||
          !shallowEqual(inst.state, nextState);
      }
    }
  }

shallowEqual的實現在shallowEqual.js中,大意就是作淺比較,也就是對象數組等只比較對象所處的地址是否相等,而不比較具體的內容,因為深層次遞歸比較對象內容是否一致很耗費性能。

結論
PureComponent是Component的子類,當PureComponent手動實現了shouldComponentUpdate方法時兩個組件沒有區別,但若沒有手動實現該方法,則PureComponent采用默認的shallowEqual比較對象是否相等性能更佳。由此可能引發的頁面不刷新現象可以采用別的辦法解決,如重新生成新的對象、采用immutable.js對象等

14 TextInput首次focus時鍵盤出現緩慢,卡頓

追查:原因就是鍵盤是懶加載模式,初次出現時需要先初始化鍵盤視圖耗費時間,要想縮減首次耗時間隔,可以事先就讓鍵盤初始化完畢。js端沒想到如何做,但是原生端可以在didFinishLaunchingWithOptions方法中寫:

  UITextField *textField = [[UITextField alloc] init];
  [self.window addSubview:textField];
  [textField becomeFirstResponder];
  [textField resignFirstResponder];
  [textField removeFromSuperview];

鍵盤消失與否

TextInput聚焦時彈出了鍵盤,點擊非TextInput空白處鍵盤是不會消失的,若想實現該功能只需要讓TextInput嵌入在ScrollView中即可。
那么問題又來了,這樣做之后,除了TextInput外屏幕上任意地方點擊鍵盤都會先消失,導致例如頁面上有個按鈕A,點擊A時會先退下鍵盤,再次點擊才能觸發A的事件,很扯淡。解決方法大體如下:

_addEvent = (event) => {
  this.events.push(event.nativeEvent.target);
};

_onStartShouldSetResponder(event) {
  const target = event.nativeEvent.target;
  if (!this.events.includes(target)) {
    Keyboard.dismiss();
  }
  return false;
}

render() {
  return (
    <ScrollView keyboardShouldPersistTaps="always">
      <View
        style={{ alignItems: 'center', flex: 1, height: SCREEN_HEIGHT }}
        onStartShouldSetResponder={(event) => this._onStartShouldSetResponder(event)}
      >
        <Button
          text="登陸"
          onLayout={(event) => this._addEvent(event)}
        />
      </View>
    </ScrollView>
  );
}

ScrollView的keyboardShouldPersistTaps屬性設置為always,則鍵盤不再攔截點擊事件,點擊空白處鍵盤不會自動消失。
onStartShouldSetResponderCapture是點擊事件發生時調用,詢問該視圖是否要攔截事件,自定義處理,當點擊屏幕除了指定位置外都退下鍵盤。指定位置A(比如登錄按鈕)點擊時,鍵盤不退下。
A的onLayout在視圖布局完成回調,event.nativeEvent.target能唯一的標識該組件。

15 redux中的reducer為何要返回Object.assign

在redux-devtools中,我們可以查看到redux下所有通過reducer更新state的記錄,每一個記錄都對應着內存中某一個具體的state,讓用戶可以追溯到每一次歷史操作產生與執行時,當時的具體狀態,這也是使用redux管理狀態的重要優勢之一.

  • 若不創建副本,redux的所有操作都將指向內存中的同一個state,我們將無從獲取每一次操作前后,state的具體狀態與改變,
  • 若沒有副本,redux-devtools列表里所有的state都將被最后一次操作的結果所取代.我們將無法追溯state變更的歷史記錄.
    創建副本也是為了保證向下傳入的this.props與nextProps能得到正確的值,以便我們能夠利用前后props的改變情況以決定如何render組件

16 使用redux時,不能在componentWillReceiveProps方法中使用action。

原因:有時候一個action改變數據后,我們希望拿到改變后的數據做另外一個action,比如初始化action讀取硬盤中的數據到內存,然后用該參數進行請求網絡數據action。此時我們可以在componentWillReceiveProps方法中拿到參數,若此時發出再發出action,則數據返回后改變reducer會再次進入componentWillReceiveProps方法,又繼續發出action,陷入死循環。可以如下解決

componentWillReceiveProps(nextProp) {
    if(nextProp.app.user && nextProp.app.sessionId && !this.isFirstLoad){
        this.props.action(nextProp.app);   // action操作
        this.isFirstLoad = true;
    }
}

17 navigation導航欄下方那根黑線是什么?

追查:發現在iphone7plus模擬器中黑線看不到,但是iphone6模擬器能看見。查看源代碼,在navigation組件中的Header.js第300行找到了黑線樣式定義,

let platformContainerStyles;
if (Platform.OS === 'ios') {
    platformContainerStyles = {
        borderBottomWidth: StyleSheet.hairlineWidth,  // hairlineWidth為當前分辨率下能顯示的最小寬度,模擬器下可能看不見
        borderBottomColor: 'rgba(0, 0, 0, .3)',
    };
} else {
    platformContainerStyles = {
        shadowColor: 'black',
        shadowOpacity: 0.1,
        shadowRadius: StyleSheet.hairlineWidth,
        shadowOffset: {
            height: StyleSheet.hairlineWidth,
        },
        elevation: 4,
    };
}

可見在ios中下方黑線使用邊框的形式實現,而安卓則是設置圖層陰影。若想隱藏該線,ios中設置headerStyle的borderBottomWidth為0,安卓中設置elevation/shadowOpacity為0.
同上,可在TabBarBottom.js中180行找到tabbar上方那跟線的默認設置,更改則可在TabNavigator中的tabBarOptions的style中設置borderTopWidth和borderTopColor

18 為何需要render的組件被保存到數組中需要設置key?

追查:跟虛擬DOM和Diff算法有關。
一次DOM操作流程包括,拿到頁面所有DOM節點,拿到css樣式表,生成render樹,布局計算節點位置,渲染等操作。 傳統應用,一個操作如果需要改變10個DOM節點,則會相應的進行10次DOM操作,很多重復浪費性能。
虛擬DOM就是剛開始就將所有的DOM節點轉換成js的相關代碼保存到內存中,一個操作改變10次DOM節點全部在內存中完成,再將內存中的js轉換為實際的DOM節點渲染,性能高。
虛擬DOM一個操作中10次改變DOM節點,每次只是改變了必要的那一個節點,不需要全部改變,為了減少時間復雜度,引入Diff算法,只比較節點改變了的那一點,進行增刪改操作等。比如現在的render樹是A、B、C節點,想再A節點后面插入D節點,若沒有key,React無法區分各個節點,只能根據渲染樹的排列依次卸載B、裝載D、卸載C、裝載B、裝載C,效率低下。如果ABC節點都有key,則React就能根據key找出對應的節點,直接渲染A、D、B、C,效率高。

19 Diffing算法相關

在任何一個單點時刻 render() 函數的作用是創建 React 元素樹。在下一個 state 或props 更新時,render() 函數將會返回一個不同的 React 元素樹。 React 通過Diffing算法找出兩顆元素樹的差異,更新必須的部分,其假定規則是:
a、DOM 節點跨層級的移動操作特別少,可以忽略不計。
b、擁有相同類的兩個組件將會生成相似的樹形結構,擁有不同類的兩個組件將會生成不同的樹形結構。
c、對於同一層級的一組子節點,它們可以通過唯一 id 進行區分。

具體的比較如下:
1、tree diff,DOM 節點跨層級的移動操作少到可以忽略不計,針對這一現象,React 通過 updateDepth 對 Virtual DOM 樹進行層級控制,只會對同一個父節點下的所有子節點。當發現節點已經不存在,則該節點及其子節點會被完全刪除掉,不會用於進一步的比較。這樣只需要對樹進行一次遍歷,便能完成整個 DOM 樹的比較。若有節點跨層級的移動,性能會受到影響
2、component diff,如果是同一類型的組件,按照原策略繼續比較 virtual DOM tree。如果不是,則將該組件判斷為 dirty component,從而替換整個組件下的所有子節點。對於同一類型的組件,有可能其 Virtual DOM 沒有任何變化,如果能夠確切的知道這點那可以節省大量的 diff 運算時間,因此 React 允許用戶通過 shouldComponentUpdate() 來判斷該組件是否需要進行 diff。
3、element diff,當節點處於同一層級時,默認情況下,當遞歸一個 DOM 節點的子節點時,React 只需同時遍歷所有的孩子節點並更改不同點,如在列表組件追加幾個item時,性能不錯。但是當如下

<ul>
  <li>1</li>
  <li>2</li>
</ul>
<ul>
  <li>3</li>
  <li>1</li>
  <li>2</li>
</ul>

React 將會改變每一個子節點而沒有意識到需要保留

  • 1
  • 2
  • 兩個子樹。這很低效。為了解決這個問題,React 支持一個 key 屬性(attributes)。當子節點有了 key ,React 使用這個 key 去比較原來的樹的子節點和之后樹的子節點。例如,添加一個 key 到我們上面那個低效的例子中可以使樹的轉換變高效

    <ul>
      <li key="2015">1</li>
      <li key="2016">2</li>
    </ul>
    <ul>
      <li key="2014">3</li>
      <li key="2015">1</li>
      <li key="2016">2</li>
    </ul>
    

    現在 React 知道有'2014' key 的元素是新的, key為'2015' 和'2016'的兩個元素僅僅只是被移動而已,效率變高很多。要注意key必須具備唯一性。若將數組中的索引作為 key ,如果存在重新排序時,性能將會很差,應該避免這種情況。

    20 高階組件(HOC)

    高階組件是重用組件邏輯的一項高級技術。高階組件並不是React API的一部分。高階組件源自於React生態。具體來說,高階組件是一個函數,能夠接受一個組件並返回一個新的組件,例如Redux的connect函數。
    HOC存在的問題:
    1、組件的靜態方法不會被傳遞,需要自行傳遞處理
    2、refs不會被傳遞,應該避免此,或者用自定義屬性傳遞
    ()
    3、react-native-fetch-blob的POST請求不成功。
    4、js傳到原生端的函數(ios中叫block)只能執行一次,否則崩潰。

    轉:


    免責聲明!

    本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



     
    粵ICP備18138465號   © 2018-2025 CODEPRJ.COM