Как ускорить работу мобильного приложения React?

React Native mobile приложение работает очень медленно при каждом нажатии. Я использую v0.40.0 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"
  }

Я сделал профилирование с помощью stacktrace в андроид-студиях и обнаружил, что mqt_js является одной из причин, которая занимает больше времени на каждом клике пользовательского интерфейса. Вы можете проверить отчет stacktrace здесь

Может ли кто-нибудь помочь мне в решении этой проблемы производительности?

Ответы

Ответ 1

Это очень широкий и основанный на мнениях вопрос, но я попытаюсь выделить most common points и предложения, основанные на profiler вы указали.

Рассматривая трассировку стека, основная проблема заключается в com.fitspot.app.debug UI Thread внутри вашего имени пакета, то есть com.fitspot.app.debug.

Как уже упоминалось здесь.

для того, чтобы отобразить рамку, все наши работы с пользовательским интерфейсом должны быть выполнены к концу этого 16 мс периода.

Как только граничный интервал установлен на 16 16ms, вы увидите, что mqt_js или mqt_js JS Thread занимают намного больше 16 16ms за один цикл, то есть ваш 16ms JS Thread работает постоянно.

В текущем profiler неясно, какие процессы выполняются в вашем JS Thread, поэтому ясно, что проблема заключается главным образом в вашем JS Code а не в UI Thread.

Существует несколько способов ускорить работу react-native приложения, которое хорошо документировано на этой странице. Здесь основной смысл в том же.

  • Сообщения об ошибках и предупреждения предоставляются в режиме dev=true, вы можете отключить их в приложении для повышения производительности.
  • Удалите все инструкции console.log из вашего приложения, поскольку это вызывает bottleneck в потоке JS. Вы можете использовать этот плагин для удаления всех команд console* как указано здесь, в ваших.babelrc файлах как

    {
      "env": {
      "production": {
      "plugins": ["transform-remove-console"]
      }
     }
    }
    
  • Вам необходимо componentize структуру вашего проекта и использовать Pure Components, чтобы полагаться только на props и state, используйте immutable data structures для более быстрого сравнения.

  • Для slower navigation transitions вам может потребоваться проверить код navigation library, поскольку в большинстве случаев у них есть timeout для default transitions по default transitions. В качестве обходного пути вы можете подумать о создании своего собственного transitioner.

  • Если вы используете Animations в своей кодовой базе, вы можете nativeDriver=true установить nativeDriver=true, что уменьшит нагрузку на ваш JS thread. Вот хорошо объясненный пример.

  • Вы также можете проверить профилирование, проверить действия JS Thead и Main Thread, хорошо объясненные на этой странице.

  • Другие вещи включают, не requiring/importing модуль, который не нужен, импортируя только требуемые classes, а не whole component.

  • Кроме того, вам не нужны external libraries чтобы создавать simple UI components, так как их производительность намного медленнее, чем собственные элементы react-native. Вы можете рассмотреть использование styled-components для компонентного UI

Ответ 2

Прежде всего, вы должны запускать свое приложение не в DEBUG. На MainApplication это делается путем изменения метода MainApplication:

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

и создание пучка:

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

Что касается кода, вот несколько советов по оптимизации реагирования:

  1. Анализ и сериализация (например, response.json() или JSON.stringify) блокирует поток js, поэтому все анимации и обработчики JS (такие как onScroll и onPress, и, конечно, метод render) страдают от этого. Попробуйте загрузить только то, что вам нужно показать.
  2. Используйте собственные анимации (useNativeDriver: true параметр), где это возможно, и старайтесь не использовать onScroll.
  3. Запишите все ваши методы render и попытайтесь сделать так, чтобы вызов этих методов был как можно редок. Он вызывается при изменении компонентов или состояния компонентов.
  4. Понимать, как работает PureComponent и пытаться использовать его там, где это необходимо. Но имейте в виду, что это также может замедлить приложение вниз, если оно не используется правильно.
  5. Всегда используйте StyleSheet для стилей, он использует их и заменяет идентификатором стиля (integer).

Плохой:

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

Хорошо:

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

// style is created once
const styles = StyleSheet.create({
    flex: { flex: 1 }
});
  1. То же самое с функциями.

Плохой:

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

Хорошо:

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

// onPressSignIn is created once
onPressSignIn = () => {
    this.props.navigator.navigate('SignIn');
}
  1. Большая O всех операций с клиентом должна быть постоянной. Перечислите массивы как можно меньше. Всегда используйте Object и Set вместо 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");

Ответ 3

  1. Используйте Flatlist над Scrollview:

    • добавьте initialNumToRender={number} prop в Flatlist, так как он отобразит только те компоненты, которые видны на экране, и отсоедините другие компоненты
  2. Используйте PureComponent в Flatlist renderItem (в вашем случае это будет каждая карта), так что они будут отображаться только при изменении их реквизита.

  3. Проверьте, повторяет ли ваш компонент повторно и снова, чтобы проверить консоль пуста либо в render(), либо в ComponentWillRecieveProps, и если это происходит, используйте функцию ShouldComponentUpdate.

  4. Удалите console.log из render() и ComponentWillRecieveProps.

Внесите эти изменения, и вы увидите, что ваша производительность намного лучше, чем раньше.

Ответ 4

Могут быть разные причины, из-за которых приложение, реагирующее на взаимодействие, замедляется. Я бы предложил следующие ключевые моменты, которые могут помочь:

  1. Предпочитайте dumb components над class components где это возможно.
  2. Попробуйте использовать redux, это мощный инструмент управления государством, который может предоставить вам лучший код, если он будет реализован должным образом.
  3. Используйте инструменты, такие как react-monocole и appr. Руководство по реакции-моноколу.
  4. Сгенерируйте подписанный apk, отлаженное приложение для реагирования имеет дополнительные функции. Вот руководство для генерации подписанного apk.

Ответ 5

Если mqt_js является основной причиной проблемы с производительностью, это означает, что в каждом клике поток JS вашего приложения имеет слишком много вещей, чтобы сделать это сразу. Коммуникация между бизнес-логикой JS и базой подчиненной области выполняется асинхронно, чем больше действий нужно закончить на стороне JS при нажатии кнопки, тем медленнее будет приложение.

Ответ, данный Притиш Вайдя, уже попадает в гвоздь по голове. Я просто хочу добавить еще 1 пункт об использовании redux в приложении. Если поток данных ваших приложений в основном выполняется с помощью redux вы можете проверить следующие вещи:

  • Если в каждом клик-событии происходит слишком много действий сокращения, попробуйте удалить ненужные или приоритизировать действия, запускающие важные анимации, а затем вызывать другие действия после завершения рендеринга новых компонентов RN. Вы можете видеть, какие действия являются нижестоящими из-за redux-logger.

  • Компоненты Break, которые слушают редукцию, хранятся в меньших, каждый из которых прослушивает другую часть магазина. Поэтому, если обновление магазина сокращено, только небольшая группа компонентов должна быть перезаписана вместо всего.

  • Используйте memoized selectors для работы с часто обновляемыми данными. reselect может помочь вам в этом случае.