最近在寫React-Native 趁着這兩天需求差很少完成了,實踐了一些優化項。javascript
記錄於此html
Performancejava
參考 React native Performancereact
打開開發者菜單(搖晃手機打開)👉 打開Show Perf Monitor
能夠看到下圖顯示框git
UI 和 JS 的幀數都穩定保持在60 爲最優狀況。github
全部的事件處理,API請求,等操做都在這個線程上,在this.setState
大量數據時,狀態的變更會致使re-render,這期間全部由JavaScript 控制的動畫都會出現卡頓掉幀npm
好比在切換路由時,幀數會有明顯抖動。此時若是有一些在componentDidMount
執行的操做就會使得路由過渡動畫很是卡頓。(後面會介紹一些能夠嘗試的解決方案json
開發環境下框架會有不少別的操做好比warning error 的輸出,類型檢測等等。react-native
若是要測試性能,最好在release
包測試。這樣更加精準。數組
開發時,會有不少console.*
指令來幫助調試。而且一些依賴庫也會有console.*
這些語句對JavaScript 線程來講是一個極大的消耗。能夠經過Babel 在生產環境中移除掉console.*
。
npm i babel-plugin-transform-remove-console --save-dev
配置.babelrc
文件
{ "env": { "production": { "plugins": ["transform-remove-console"] } } }
<FlatList />
FlatList
組件更加適合來展現長列表,而且指定合適的 getItemLayout
方法, getItemLayout
會跳過渲染Item 時的佈局計算,直接使用給定的配置(詳情查看連接☝️)
在框架執行編寫好的業務代碼前,須要把在內存中加載並解析代碼,代碼量越大這個過程就更耗時,致使首屏渲染速度過慢。並且每每會出現一些頁面或者組件根本不會被用戶訪問到。這時能夠經過懶加載來優化。
官網有給出例子
VeryExpensive.js
import React, { Component } from 'react'; import { Text } from 'react-native'; // ... import some very expensive modules // You may want to log at the file level to verify when this is happening console.log('VeryExpensive component loaded'); export default class VeryExpensive extends Component { // lots and lots of code render() { return <Text>Very Expensive Component</Text>; } }
Optimized.js
import React, { Component } from 'react'; import { TouchableOpacity, View, Text } from 'react-native'; let VeryExpensive = null; //定義變量 export default class Optimized extends Component { state = { needsExpensive: false }; // 定義內部狀態來控制組件是否須要加載 didPress = () => { // 在觸發須要加載組件的事件時 if (VeryExpensive == null) { // 不重複引用 VeryExpensive = require('./VeryExpensive').default; // 把組件的引用賦給定義好的變量 } this.setState(() => ({ needsExpensive: true, // 更改控制的狀態,觸發組件re-render })); }; render() { return ( <View style={{ marginTop: 20 }}> <TouchableOpacity onPress={this.didPress}> <Text>Load</Text> </TouchableOpacity> {this.state.needsExpensive ? <VeryExpensive /> : null} </View> ); } }
React 在內部state
或者外部傳入的props
發生改變時,會從新渲染組件。若是在短期內有大量的組件要從新渲染就會形成嚴重的性能問題。這裏有一個能夠優化的點。
PureComponent
讓組件本身比較props
的變化來控制渲染次數,實踐下來這種可控的方式比純函數組件要靠譜。或者在Component
中使用 shouldComponentUpdate
方法,經過條件判斷來控制組件的更新/從新渲染。PureComponent
時要注意這個組件內部是淺比較狀態,若是props
的有大量引用類型對象,則這些對象的內部變化不會被比較出來。因此在編寫代碼時儘可能避免複雜的數據結構JavaScript 單線程,要利用好它的異步特性,和一些鉤子回調。
好比上面提到路由切換時componentDidMount
中的操做會致使卡頓,這裏可使用 InteractionManager.runAfterInteractions()
將須要執行的操做放到runAfterInteractions
的回調中執行。
componentDidMount() { InteractionManager.runAfterInteractions(() => { // your actions }) }
須要注意的是 InteractionManager
是監聽全部的動畫/交互 完成以後纔會觸發 runAfterInteractions
中的回調,若是項目中有一些長時間動畫或者交互,可能會出現長時間等待。因此 因爲 InteractionManager
的不可控性,使用的時候要根據實際狀況調整。
在react-native 中的一些動畫反饋,好比TouchableOpacity
在觸摸時會響應 onPress
而且 自身的透明度會發生變化,這個過程當中若是 onPress
中有複雜的操做,極可能會致使組件的透明反饋卡頓,這時能夠將onPress
中的操做包裹在 requestAnimationFrame
中。這裏給出一個個人實踐(利用styled-component)
import styled from 'styled-components' export const TouchableOpacity = styled.TouchableOpacity.attrs({ onPress: props => () => { requestAnimationFrame(() => { props.onPressAsync && props.onPressAsync() }, 0) } })``
這裏把onPress
改爲在 requestAnimationFrame
的回調中執行onPressAsync
傳入的操做。
同理,還在FlatList
的onReachEnd
實踐了這個操做,來避免iOS 中滾動回彈時執行操做的卡頓。
以上,記錄了近期寫React-Native 的一些實踐過的優化項。
路漫漫其修遠兮,吾將上下而求索
May love & peace be with you
本文做者: Roy Luo