最近在寫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