react native一套代碼能夠開發出跨平臺app, 減小了人力、節省了時間、避免了 iOS 與 Android 版本發佈的時間差,開發新功能能夠更迅速。等等css
缺點:內存、轉化爲原生的html
props state refs 方面回答react
主要考察flex佈局絕對定位問題android
redux ==> action/reducer/store
mobx ==>數據雙向綁定ios
當調用 setState 時,React會作的第一件事情是將傳遞給 setState 的對象合併到組件的當前狀態。 這將啓動一個稱爲和解(reconciliation
)的過程。 和解(reconciliation)的最終目標是以最有效的方式,根據這個新的狀態來更新UI。 爲此,React將構建一個新的 React 元素樹(您能夠將其視爲 UI 的對象表示)。 一旦有了這個樹,爲了弄清 UI 如何響應新的狀態而改變,React 會將這個新樹與上一個元素樹相比較( diff )。 經過這樣作, React 將會知道發生的確切變化,而且經過了解發生什麼變化,只需在絕對必要的狀況下進行更新便可最小化 UI 的佔用空間。算法
簡單地說,一個 React element 描述了你想在屏幕上看到什麼。 換個說法就是,一個 React element 是一些 UI 的對象表示。 一個 React Component 是一個函數或一個類, 它能夠接受輸入並返回一個 React element (一般是經過 JSX ,它被轉化成一個 createElement 調用)。redux
其實這個問題也是跟reconciliation有關係。 「和解( reconciliation )的最終目標是以最有效的方式,根據新的狀態更新用戶界面」。 若是咱們知道咱們的用戶界面(UI)的某一部分不會改變, 那麼沒有理由讓 React 很麻煩地試圖去弄清楚它是否應該渲染。 經過從 shouldComponentUpdate 返回 false, React 將假定當前組件及其全部子組件將保持與當前組件相同react-native
爲了解決跨瀏覽器兼容性問題, 您的 React 中的事件處理程序將傳遞 SyntheticEvent
的實例, 它是 React 的瀏覽器本機事件的跨瀏覽器包裝器。 這些 SyntheticEvent 與您習慣的原生事件具備相同的接口,除了它們在全部瀏覽器中都兼容。 有趣的是,React 實際上並無將事件附加到子節點自己。 React 將使用單個事件監聽器監聽頂層的全部事件。 這對於性能是有好處的,這也意味着在更新DOM時,React 不須要擔憂跟蹤事件監聽器數組
this.props.children 的值有三種可能瀏覽器
系統提供React.Children.map()
方法安全的遍歷子節點對象
XSS是一種跨站腳本攻擊
,是屬於代碼注入的一種,攻擊者經過將代碼注入網頁中,其餘用戶看到會受到影響(代碼內容有請求外部服務器);
CSRF是一種跨站請求僞造
,冒充用戶發起請求,完成一些違背用戶請求的行爲(刪帖,改密碼,發郵件,發帖等)
ES6引入了一種新的原始數據類型Symbol,表示獨一無二的值。 Symbol函數前不能使用new命令,不然會報錯。這是由於生成的Symbol是一個原始類型的值,不是對象 Symbol函數能夠接受一個字符串做爲參數,表示對Symbol實例的描述,主要是爲了在控制檯顯示,或者轉爲字符串時,比較容易區分。
追查:這兩個組件的實如今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對象等
追查:緣由就是鍵盤是懶加載模式,初次出現時須要先初始化鍵盤視圖耗費時間,要想縮減首次耗時間隔,能夠事先就讓鍵盤初始化完畢。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能惟一的標識該組件。
在redux-devtools中,咱們能夠查看到redux下全部經過reducer更新state的記錄
,每個記錄都對應着內存中某一個具體的state,讓用戶能夠追溯到每一次歷史操做產生與執行時,當時的具體狀態,這也是使用redux管理狀態的重要優點之一.
緣由:有時候一個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; } }
追查:發如今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
追查:跟虛擬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,效率高。
在任何一個單點時刻 render() 函數的做用是建立 React 元素樹。在下一個 state 或props 更新時,render() 函數將會返回一個不一樣的 React 元素樹。 React 經過Diffing算法找出兩顆元素樹的差別,更新必須的部分,其假定規則是:
a、DOM 節點跨層級的移動操做特別少,能夠忽略不計。
b、擁有相同類的兩個組件將會生成類似的樹形結構,擁有不一樣類的兩個組件將會生成不一樣的樹形結構。
c、對於同一層級的一組子節點,它們能夠經過惟一 id 進行區分。
具體的比較以下:
一、tree diff
,DOM 節點跨層級的移動操做少到能夠忽略不計,針對這一現象,React 經過 updateDepth 對 Virtual DOM 樹進行層級控制,只會對同一個父節點下的全部子節點。當發現節點已經不存在,則該節點及其子節點會被徹底刪除掉,不會用於進一步的比較。這樣只須要對樹進行一次遍歷,便能完成整個 DOM 樹的比較。如有節點跨層級的移動,性能會受到影響
二、component diff
,若是是同一類型的組件,按照原策略繼續比較 virtual DOM tree。若是不是,則將該組件判斷爲 dirty component,從而替換整個組件下的全部子節點。對於同一類型的組件,有可能其 Virtual DOM 沒有任何變化,若是可以確切的知道這點那能夠節省大量的 diff 運算時間,所以 React 容許用戶經過 shouldComponentUpdate() 來判斷該組件是否須要進行 diff。
三、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 將會改變每個子節點而沒有意識到須要保留
<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 ,若是存在從新排序時,性能將會不好,應該避免這種狀況。
高階組件是重用組件邏輯的一項高級技術。高階組件並非React API的一部分。高階組件源自於React生態。具體來講,高階組件是一個函數,可以接受一個組件並返回一個新的組件,例如Redux的connect函數。
HOC存在的問題:
一、組件的靜態方法不會被傳遞,須要自行傳遞處理
二、refs不會被傳遞,應該避免此,或者用自定義屬性傳遞
()
三、react-native-fetch-blob的POST請求不成功。
四、js傳到原生端的函數(ios中叫block)只能執行一次,不然崩潰。
轉: