【问题标题】:How to make React Native mobile application faster?如何让 React Native 移动应用程序更快?
【发布时间】:2018-03-26 13:02:02
【问题描述】:

React Native 移动应用程序在每次点击时运行非常缓慢。 我正在使用 react native v0.40.0 以下是我项目的依赖项。

{
    "analytics-react-native": "^1.1.0",
    "apisauce": "^0.7.0",
    "babel-preset-es2015": "^6.18.0",
    "es6-promise": "^4.0.5",
    "flow-bin": "^0.36.0",
    "geolib": "^2.0.22",
    "immutable": "^3.8.1",
    "intl": "^1.2.5",
    "isomorphic-fetch": "^2.2.1",
    "lodash": "^4.17.4",
    "lodash.range": "^3.2.0",
    "prop-types": "^15.5.10",
    "raven-js": "^3.13.1",
    "react": "^15.4.2",
    "react-native": "^0.40.0",
    "react-native-apple-healthkit-rn0.40": "^0.3.2",
    "react-native-blur": "^2.0.0",
    "react-native-button": "^1.7.1",
    "react-native-checkbox": "^2.0.0",
    "react-native-code-push": "^1.17.3-beta",
    "react-native-datepicker": "^1.4.4",
    "react-native-device-info": "^0.10.1",
    "react-native-easy-toast": "^1.0.6",
    "react-native-fbsdk": "^0.5.0",
    "react-native-geocoder": "^0.4.5",
    "react-native-gifted-chat": "^0.1.3",
    "react-native-global-props": "^1.1.1",
    "react-native-image-crop-picker": "^0.15.1",
    "react-native-image-picker": "^0.25.1",
    "react-native-image-slider": "^1.1.5",
    "react-native-keyboard-aware-scroll-view": "^0.2.7",
    "react-native-maps": "0.15.2",
    "react-native-modal-dropdown": "^0.4.4",
    "react-native-popup-menu": "^0.7.2",
    "react-native-push-notification": "^2.2.1",
    "react-native-radio-buttons": "^0.14.0",
    "react-native-router-flux": "3.38.0",
    "react-native-segmented-android": "^1.0.4",
    "react-native-snap-carousel": "2.1.4",
    "react-native-stars": "^1.1.0",
    "react-native-swipeout": "^2.2.2",
    "react-native-swiper": "^1.5.4",
    "react-native-tableview-simple": "0.16.5",
    "react-native-vector-icons": "^4.0.0",
    "react-native-video": "^1.0.0",
    "react-native-zendesk-chat": "^0.2.1",
    "react-redux": "^4.4.6",
    "recompose": "^0.20.2",
    "redux": "^3.5.2",
    "redux-thunk": "^2.0.1"
  }

我在 android studios 中使用 stacktrace 进行了分析,发现 mqt_js 是每次 UI 点击都需要更多时间的原因之一。你可以查看stacktrace报告here

谁能帮我解决这个性能问题?

【问题讨论】:

  • 这些延迟是否发生在您的应用的生产版本中?还是只是开发构建?您是否尝试过使用 redux-logger 之类的东西来查看您的所有操作都被触发了?
  • 它在生产和开发版本中都出现了延迟。我已经在使用redux-logger
  • 尝试在IOS上使用systrace,它可能会显示一些东西。从(android)的附加痕迹来看,在 UI 线程(全绿色 F)中没有看到丢帧,但在单击后 JS 线程(mqt_js)中似乎有很多工作正在进行。不幸的是,android 上的 systrace 被破坏以提供 JS 线程的详细信息。如果可以的话,附上 ios systrace。

标签: android performance react-native redux stack-trace


【解决方案1】:

这里有一些优化 react-native 的建议:

  1. 解析和序列化(如response.json()JSON.stringify)会阻塞js线程,所以所有的JS动画和处理程序(如onScrollonPress,当然还有render方法)都会受此影响。尝试仅从后端加载您需要显示的数据。
  2. 尽可能使用原生动画(useNativeDriver: true 参数),尽量不要使用onScroll。另外,请考虑将react-native-reanimated 用于动画。
  3. 优化您的render 方法并尝试使其调用尽可能少。当组件的 props 或 state 改变时调用它。
  4. 了解PureComponentReact.memo 的工作原理,并在必要时尝试使用它们。但请注意,如果使用不当,它们也会降低应用速度。
  5. 始终将StyleSheet 用于样式,它将它们兑现并替换为样式ID(整数),因此样式仅传递给本机端一次,然后使用样式ID。

不好:

// style object is created on every render
render() {
    return <View style={{flex:1}}/>
}

好:

render() {
    return <View style={styles.flex}/>
}

// style is created once
const styles = StyleSheet.create({
    flex: { flex: 1 }
});
  1. 与函数相同。

不好:

// onPress handler is created on every render
render() {
    return <TouchableOpacity onPress={() => this.props.navigator.navigate('SignIn')}/>
}

好:

render() {
    return <TouchableOpacity onPress={this.onPressSignIn}/>
}

// onPressSignIn is created once
onPressSignIn = () => {
    this.props.navigator.navigate('SignIn');
}
  1. Big O 客户端上的所有操作都应该保持不变。尽可能少地枚举数组。尽可能使用ObjectSet 而不是Array。当您需要从服务器/数据库加载大量数据、将排序和其他繁重的计算留给服务器时,请使用分页。

比如如果你经常需要通过id获取对象,最好使用:

let items = {
    "123": { id: "123", ... },
    "224": { id: "224", ... }
};

let item = items["123"];

而不是通常的数组:

let items = [
    0: { id: "123", ... },
    1: { id: "224", ... }
];

let item = items.find(x => x.id === "123");
  1. 查看此文档:https://reactnative.dev/docs/performance

【讨论】:

    【解决方案2】:

    这是一个非常广泛基于意见的问题,但我会尝试突出most common points 和基于您列出的profiler 的建议。

    查看您的堆栈跟踪,主要问题在于包名称中的UI Thread,即com.fitspot.app.debug

    正如提到的here

    为了显示一个框架,我们所有的 UI 工作都需要在 16 毫秒周期结束前完成。

    一旦边界间隔设置为16ms,那么您可以看到mqt_jsJS Thread 在一个周期内花费的时间远远超过16ms,这意味着您的JS Thread 一直在运行。

    在当前的profiler中,不清楚你的JS Thread中执行了哪些进程,因此很明显问题主要出在你的JS Code而不是UI Thread

    有多种方法可以让react-native 应用程序更快,这在page 中有详细记录。这是一个基本的要点。

    • 错误和警告消息以dev=true 模式提供,您可以在应用程序中禁用它们以获得更好的性能。
    • 从您的应用程序中删除所有 console.log 语句,因为它会在 JS 线程上导致 bottleneck。你可以使用这个插件来删除所有console* 中提到的here 语句,在你的.babelrc 文件中作为

      {
        "env": {
        "production": {
        "plugins": ["transform-remove-console"]
        }
       }
      }
      
    • 你需要componentize你的项目结构,并使用Pure Components,仅依赖propsstate,使用immutable data structures进行更快的比较。

    • 对于slower navigation transitions,您可能需要检查navigation library 代码,因为大多数情况下它们都有timeout 对应default transitions。作为一种解决方法,您可以考虑构建自己的transitioner

    • 如果您在代码库中使用Animations,您可以考虑设置nativeDriver=true,这将减少JS thread 的负载。这是一个很好的解释example

    • 您可能还想检查 Profiling,检查 JS TheadMain Thread 操作,在此页面上有详细说明。

    • 其他东西包括,不是requiring/importing模块,这不是必需的,只需要导入classes,而不是whole component

    • 另外,你不需要external libraries 来生成simple UI components,因为它们的性能比react-native 的原生元素慢得多。您可以考虑使用styled-components 组件化您的UI

    【讨论】:

      【解决方案3】:
      1. 在滚动视图上使用Flatlist

        • initialNumToRender={number} 属性添加到 Flatlist,因为它只会显示那些在屏幕上可见的组件并分离其他组件
      2. 在 Flatlist 的 renderItem 中使用 PureComponent(在你的情况下,它是每个卡片),这样它们只会在它们的道具发生变化时呈现。

      3. 检查您的组件是否一次又一次地重新渲染,以便在 render() 或 ComponentWillRecieveProps 中测试放置控制台,如果发生这种情况,请使用 ShouldComponentUpdate。

      4. 从 render() 和 ComponentWillRecieveProps 中删除 console.log。

      进行这些更改,您会发现自己的表现比以前好很多。

      【讨论】:

        【解决方案4】:

        可能有多种原因导致 react-native 应用程序变慢。我建议以下可以提供帮助的关键点:

        1. 尽可能首选dumb components 而不是class components
        2. 尝试使用redux,它是一个强大的状态管理工具,如果实施得当,可以为您提供最好的代码。
        3. 使用react-monocoleappr 等工具。 react-monocole 指南。
        4. 生成已签名的 apk,调试后的 react-native 应用程序具有附加功能。这是generate signed apk 的指南。

        【讨论】:

        【解决方案5】:

        如果mqt_js是导致性能问题的主要原因,这意味着在每次点击中,你的应用程序的JS线程一次要做的事情太多了。 JS业务逻辑和underlay原生realm之间的通信是异步进行的,按下按钮时需要在JS端完成的动作越多,应用就越慢。

        Pritish Vaidya 给出的Answer 已经一针见血了。我只想在您的应用程序中再添加 1 点关于 redux 的使用情况。如果您的应用程序的数据流主要使用redux 完成,那么您可以检查以下内容:

        • 如果每次点击事件上发生的redux动作过多,请尝试删除不必要的动作或优先考虑触发重要动画的动作,然后在新的RN组件完成渲染后触发其他动作。您可以通过redux-logger 来查看哪些操作是低级操作。

        • 将监听 redux 存储的组件分解成更小的组件,每个组件监听存储的不同部分。所以如果 redux 的 store 更新了,只需要重新渲染一小部分组件,而不是全部。

        • 使用记忆选择器来处理频繁更新的数据。 reselect 在这种情况下可以为您提供帮助。

        【讨论】: