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