How to make React Native mobile application faster

2020-05-23 06:52发布

React Native mobile application is working very slow on every click. I am using react native v0.40.0 and following are the dependencies of my project.

{
    "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"
  }

I did profiling with stacktrace in android studios, and found that mqt_js is one of the reason which takes more time on every UI clicks. You can check stacktrace report here

Can anybody help me in solving this performance issue.?

5条回答
萌系小妹纸
2楼-- · 2020-05-23 07:22
  1. Use Flatlist over Scrollview:

    • add initialNumToRender={number} prop to Flatlist, as it will show only those components which are visible on screen and detach the other components
  2. Use PureComponent in Flatlist renderItem (In your case it will Each Card), so that they will only render whenever their props get changed.

  3. Check whether your component is re-rendering again and again in order to test put console either in render() or in ComponentWillRecieveProps and if this is happening then use ShouldComponentUpdate.

  4. Remove console.log from render() and ComponentWillRecieveProps.

Make these changes and you see your performance is much better than before.

查看更多
可以哭但决不认输i
3楼-- · 2020-05-23 07:25

There can be various reasons which makes the react-native app slower. I would suggest the following key points that can help:

  1. Prefer dumb components over class components wherever possible.
  2. Try using redux, it is a powerful state management tool that can provide you the best code, if implemented properly.
  3. Use tools like react-monocole and appr. Guide to react-monocole.
  4. Generate the signed apk, the debugged react-native app has additional features in it. Here's a guide to generate signed apk.
查看更多
手持菜刀,她持情操
4楼-- · 2020-05-23 07:28

This is a very broad and opinion based question, but I'll try to highlight the most common points and suggestions based on the profiler you have listed.

Looking at your stack trace, the main problem lies with the UI Thread inside your package name ie com.fitspot.app.debug.

As mentioned here.

in order to display a frame, all our UI work needs to be done by the end of that 16ms period.

Once the boundary interval is set to 16ms, then you can see that the mqt_js or the JS Thread is taking far longer than 16ms for one cycle, meaning your JS Thread is running constantly.

In the current profiler, it is unclear what processes are executed in your JS Thread, therefore it is clear that the problem lies mainly in your JS Code and not the UI Thread.

There are multiple ways to make the react-native app faster which is well documented in this page. Here's a basic gist to the same.

  • Error and warning messages are provided in the mode dev=true, you can disable them across the app for a better performance.
  • Remove all the console.log statements from your app, as it causes a bottleneck on the JS Thread. You can use this plugin to remove all the console* statements as mentioned here, in your .babelrc files as

    {
      "env": {
      "production": {
      "plugins": ["transform-remove-console"]
      }
     }
    }
    
  • You need to componentize your project structure, and use Pure Components , to rely on props and state only, use immutable data structures for faster comparisons.

  • For the slower navigation transitions, you might want to check the navigation library code, since mostly they have a timeout for default transitions. As a workaround you may consider building your own transitioner.

  • If you're using Animations in your codebase, you might consider setting nativeDriver=true, which would reduce the load on your JS thread. Here's a well explained example.

  • You also might want to check the Profiling, to check the JS Thead and the Main Thread operations, well explained on this page.

  • Other stuff includes, not requiring/importing the module, which is not necessary, importing only classes required, and not the whole component.

  • Also , you dont need external libraries to make simple UI components, since their performance is much slower than the native elements of react-native. You may consider using styled-components to componentize your UI

查看更多
祖国的老花朵
5楼-- · 2020-05-23 07:30

If mqt_js is the main cause of performance issue, that means in every click, the JS thread of your app has too many things to do at once. Communication between JS business logic and underlay native realm is done asynchronously, the more actions need to be finished in JS side when a button is pressed, the slower the app will be.

Answer given by Pritish Vaidya already hits the nail on the head. I just want to include 1 more point about the usage of redux in your app. If your app's data flow is mainly done using redux then you can check for following things:

  • If there are too many redux actions happening on each clicking event, try to remove unnecessary ones or prioritise actions triggering important animations first, then triggering other actions once new RN components finished rendering. You can see which actions are bottomneck ones by redux-logger.

  • Break components listening to redux's store into smaller ones, with each listening to a different part of the store. So if redux's store is updated, only a small group of components should be rerendered instead of everything.

  • Use memoized selectors to deal with frequently updated data. reselect can help you in this case.

查看更多
我欲成王,谁敢阻挡
6楼-- · 2020-05-23 07:45

First of all you should run your app not in DEBUG. On android it is done by changing MainApplication method:

@Override
public boolean getUseDeveloperSupport() {
  return false; // BuildConfig.DEBUG;
}

and making bundle:

react-native bundle --platform android --dev false --entry-file ./index.js --bundle-output ./android/app/src/main/assets/index.android.bundle --assets-dest ./android/app/src/main/res/ --sourcemap-output ./android/app/src/main/assets/index.android.map

As for the code, here are some advices for optimization react-native:

  1. Parsing and serializing (such as response.json() or JSON.stringify) blocks js thread, so all JS animations and handlers (such as onScroll and onPress, and of course render method) suffer from this. Try to load only what you need to show.
  2. Use native animations (useNativeDriver: true parameter) where it is possible, and try not to use onScroll.
  3. Log all your render methods and try to make the call of these methods as rare as possible. It is called when component's props or state changed.
  4. Understand how PureComponent works and try to use it where necessary. But keep in mind that it also can slow the app down if not used correctly.
  5. Always use StyleSheet for styles, it cashes them and replaces with style id (integer).

Bad:

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

Good:

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

// style is created once
const styles = StyleSheet.create({
    flex: { flex: 1 }
});
  1. Same with functions.

Bad:

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

Good:

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

// onPressSignIn is created once
onPressSignIn = () => {
    this.props.navigator.navigate('SignIn');
}
  1. Big O of all operations on client should be constant. Enumerate arrays as less as you can. Always use Object and Set instead of Array where it is possible. Use pagination when you need to load big amounts of data from server / database, leave sorting and other heavy calculations for server.

For example if you often need to get objects by id, it is better to use:

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

let item = items["123"];

instead of usual array:

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

let item = items.find(x => x.id === "123");
查看更多
登录 后发表回答