Практика React Native в «Энергетическом чтении»

Android iOS React.js React Native
Практика React Native в «Энергетическом чтении»

Автор этой статьи: Front-end команда China Literature Group

Исходное заявление: Эта статья подготовлена ​​членами команды интерфейса чтения YFE, пожалуйста, уважайте оригинальность, пожалуйста, свяжитесь с общедоступной учетной записью (id: yuewen_YFE) для авторизации и укажите автора, источник и ссылку.

предисловие

После более чем трех месяцев интенсивной разработки двухмерный продукт China Literature Group APP «Yuanqi Reading», наконец, появился на полках крупных магазинов приложений. Большинство функциональных модулей приложения «Yuanqi Reading» основаны наReact NativeРазработка, фронтенд-команда прошла через множество ям React Native за весь процесс разработки, а также накопила много практического опыта, которым можно поделиться с вами.

1. Предыстория бизнеса и выбор технологий

Перед использованием React Native (далее именуемый RN), как и у большинства команд в отрасли, модель разработки нашего приложения использует гибридную модель разработки с H5, встроенным в клиент (iOS/Android). В начале, в дополнение к использованию относительно зрелого автономного пакетного решения для управления статическими ресурсами, мы также проделали большую работу по оптимизации загрузки первого экрана, но обнаружили, что между онлайн-опытом H5 и онлайн-опытом все еще существует большой разрыв. Данные производительности и оригинальны, поэтому решили ввести новую схему.

RN и Weex уже являются двумя относительно зрелыми гибридными решениями в отрасли, которые в основном отвечают нашим потребностям:

  • Пользовательский опыт: по сравнению со страницей H5, RN и Weex значительно улучшили пользовательский интерфейс, и он почти близок к оригиналу.
  • Затраты на оплату труда: По сравнению с клиентом, набор кодов RN и Weex может работать на обоих концах iOS и Android, а также выше коэффициент повторного использования кода.
  • Гибкий выпуск: И RN, и Weex имеют возможность горячего обновления.

В конце концов мы выбрали RN в качестве решения, в основном учитывая несколько факторов:

  • Статус сообщества: По сравнению с Weex активность сообщества RN и окружающая экология Facebook React лучше.
  • одобрение Дачанга: Tencent, JD.com, Baidu и Ctrip имеют крупные онлайн-продукты.
  • Статус команды: еще в первой половине 2017 года фронтенд-команда Yuewen выбрала React в качестве основного стека технологий НИОКР в нашей линейке фронтенд-продуктов, и большинство участников могут управлять React.

2. Сценарии применения

В приложении «Yuanqi Reading» сценарии приложений, разработанные с использованием RN, достигли примерно 70%. На страницах, которые могут видеть пользователи, кроме книжной полки, регистрации и входа в систему и механизма чтения, почти все остальные модули разработаны с использованием RN.APP «Жизненное чтение» уже является масштабным приложением RN среди крупных отечественных продуктов. . Добро пожаловать во все магазины приложений (iOS,Android) найдите «Yuanqi Reading», чтобы загрузить опыт.

▲小说书城

▲Книжный магазин художественной литературы

▲漫画书城

▲Город комиксов

▲元气圈

▲Круг жизненной силы

▲漫画详情

▲ Детали комиксов

▲排行榜

▲ Таблица лидеров

▲分类

▲Классификация

3. Управление навигацией

Для разработки РН очень важно предварительное планирование навигации, которое обычно нужно учитывать заранее при построении проекта. Что касается выбора компонентов навигации,react-navigationэто хороший выбор, мы надеемсяreact-navigationЭто может быть более общим в бизнес-сценариях.

1. Единые правила прыжка

Взаимное переключение между Native и RN является наиболее распространенным требованием. С унифицированным URL-адресом вы можете легко переключаться между Native и RN, сохраняя карту сайта и реализуя открытый интерфейс.

react-navigationэто использоватьrouteName + paramsФорма прыгает, значит надо звонитьrouter.getStateForActionПрежде чем сделать небольшую настройку:

// 修正 action: 允许 navigate/push/reset 动作传 url
if (isPushLikeAction(action) || isReplaceAction(action)) {
  if (isRouteUrl(action.routeName)) {
    // 使用 path-to-regexp 库来判断 url 对应的 routeName + params
    const route = parseRouteByUrl(action.routeName) 
    if (route) {
      action.routeName = route.name
      action.params = route.params
    }
  }
}

2. Реализовать прыжок 404

В веб-разработке очень распространена логика страницы 404. Ссылаясь на описанный выше метод, RN может реализовать его следующим образом:

// 修正 action: 当 navigate/push/replace 跳转到未知 routeName 时,调整为定义的 notFoundRouteName
if (isPushLikeAction(action) || isReplaceAction(action)) {
  // 修正 action: 提供 404 能力
  if (allRouteNames.indexOf(action.routeName) === -1) {
    const oldAction = { ...action }
    action.routeName = notFoundRouteName
    action.params = { action: oldAction }
  }
}

3. Контролируйте жизненный цикл страницы

В процессе разработки проекта мы часто сталкиваемся с таким требованием.После возврата на исходную страницу нам необходимо обновить данные исходной страницы, например, после авторизации войти на страницу сведений и вернуться на страницу списка после завершение операции.

Когда проект «Чтение энергии» только стартовалreact-navigationИли версия 0.x, используйте толькоonNavigationStateChange + contextсделать страницу узнаваемойfocus/blur. После версии 1.x мы можем прослушивать события didFocus или didBlur с помощью встроенного метода addListener.

4. Оптимизируйте страницу, чтобы она открывалась во второй раз

"Vital Qi Reading" - это приложение с RN в качестве входа. В процессе обычного использования необходимо часто переключаться с RN на Native или с Native на RN, чтобы было несколько страниц RN (корневых компонентов) и the first Когда два корневых компонента инициализируются, им необходимо определить местонахождение указанной страницы, по согласованию с Native, черезinitialRouteUrlилиinitialRouteName + initialRouteParamsЧтобы указать RN, на какую страницу ориентироваться:

const navigator = getActiveNavigator() // 需要全局维护一个 Navigator 的堆栈
let nextState = originGetStateForAction(action, state) // 调用原始的 getStateForAction 获取新的/初始化的状态

if (navigator) {
  const { initialRouteName, initialRouteParams, goBackOnTop } = navigator.props // 读取 navigator 的 props

  if (isInitAction(action)) {
    // 支持通过 initialRouteName & initialRouteParams 初始化到相应页面
    if (initialRouteName) {
      const initialActionPayload = { routeName: initialRouteName, params: initialRouteParams }
      const initialAction = NavigationActions.navigate(initialActionPayload)
      nextState = router.getStateForAction(initialAction, nextState) 
       if (!isTopNavigator() && nextState.index > 0) {
        // 非第一层 RN 实例且有两个页面的时候(前面 navigate 到了非一级页面),保留最后一个页面
        nextState = {
          ...nextState,
          index: 0,
          routes: nextState.routes.slice(-1),
        }
      }
    }
  } else if (isBackAction(action)) {
    // 在第一层页面,并且不是是第一个 Navigator,则调用 goBackOnTop 关闭 RN 
    if (isTopScren(state) && !isTopNavigator( ) && typeof goBackOnTop === 'function') { 
      goBackOnTop()
      if (nextState === state) {
        // 防止 Android 的物理返回键导致退出 App
        nextState = { ...nextState }
      }
    }
  }
}

return nextState

5. Государственное локальное хранилище

компонентыreact-navigationВ версии 2.x добавлена ​​функция локального хранения состояния, после перезагрузки можно сразу найти предыдущую страницу, но нужно обратить внимание на два момента:

  • В бизнес-сценарии с несколькими корневыми компонентами, такими как «Чтение юаньци», каждый корневой компонентrootNavigatorДолжна быть маркировка для различения, рекомендуется различать по индексу
  • После ошибки страницы (красный экран), во избежание перезагрузки или остановки на текущей странице ошибки, вы можетеcomponentDidCatchочистить локальное хранилище

В-четвертых, управление состоянием и сохранение данных

В «Чтении юаньци» нам часто нужно кэшировать информацию пользователя, сведения о книгах, которые были просмотрены, а также сообщения, полученные пользователем и т. д., чтобы пользователи могли избежать белого экрана или нештатных ситуаций при доступе к «Юаньци». Чтение» в автономном режиме. А также можно добиться «открытия за секунды».

Например, когда пользователь открывает страницу сведений о книге в первый раз, информация о деталях книги кэшируется, а когда пользователь открывает ее во второй раз, может быть достигнут эффект открытия в секундах. Эффект открытия в секундах можно увидеть на следующем рисунке:

▲跳转详情页

▲ Перейти на страницу сведений

Мы выбираемreduxиredux-persistИспользуется вместе для обеспечения совместного использования данных и постоянного кэширования данных.

1. Редукс

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

При написании редукса вы можете почувствовать, что вам нужно написать много шаблонного кода. порекомендуйте это здесьredux-actionsЭта библиотека может помочь нам уменьшить объем кода. Вот простой пример:

// 常见的写法
export default (state = {}, action) => {
 switch (action.type) {
  case INCREASE:
   return {...state, total: state.total + 1}
  case DECREASE:
   return {...state, total: state.total - 1}
  default:
   return state
 }
}

// 通过 handleActions 方法
import { handleActions } from 'redux-actions'

export default handleActions({
 [INCREASE]: state => {
  ...state,
  total: state.total + 1
 },
 [DECREASE]: state => {
  ...state,
  total: state.total - 1
 }
}, initialState = {})

2. редукс-сохраняется

redux-persistМагазин будет подписан, и как только магазин изменится, операция хранилища будет запущена. Таким образом, когда мы работаем с магазином, данные будут обновляться локально.

При разработке проекта вы можете обнаружить, что некоторые данные, которыми мы делимся в магазине, могут не нуждаться в локальном кэшировании. Например, на странице результатов поиска, поскольку ключевые слова каждого поиска разные, результаты тоже разные, поэтому кэшировать такие данные локально бессмысленно. Итак, как мы можем контролировать, чтобы некоторые данные не кэшировались локально?

redux-persistПоддержка конфигурацииЧерный и белый список, значениеСохранять только данные из белого спискаилиНе сохранять данные в черном списке. Таким образом, черный и белый список можно настроить в соответствии с требованиями, чтобы решить, какие данные необходимо кэшировать локально, а какие нет. Например:

import { createStore, applyMiddleware, combineReducers } from 'redux'
import { persistReducer } from 'redux-persist'
import thunkMiddleware from 'redux-thunk'
import storage from 'redux-persist/lib/storage'

const rootPersistConfig = {
 storage,
 key: '***',
 blacklist: ['***'] // 黑名单
}

const enhancer = applyMiddleware(thunkMiddleware)
export const store = createStore(persistReducer(rootPersistConfig, rootReducer), enhancer)
export const persistor = persistStore(store)

5. Оптимизация производительности

A compelling reason for using React Native instead of WebView-based tools is to achieve 60 frames per second and a native look and feel to your apps. Where possible, we would like for React Native to do the right thing and help you to focus on your app instead of performance optimization, but there are areas where we're not quite there yet, and others where React Native (similar to writing native code directly) cannot possibly determine the best way to optimize for you and so manual intervention will be necessary. We try our best to deliver buttery-smooth UI performance by default, but sometimes that just isn't possible.

Я видел интерпретацию производительности в документе RN, в котором упоминалось: «В настоящее время в некоторых случаях RN не может решить, как оптимизировать за вас (неизбежно писать собственный код), поэтому ручное вмешательство по-прежнему необходимо. », мы действительно потратили много усилий на оптимизацию производительности.

1. Оптимизация первого экрана

Учащимся, запустившим проект RN, нетрудно обнаружить, что когда мы впервые заходим на страницу RN, появляется короткий белый экран, который длится от десятков миллисекунд до 1–2 секунд. , Время белого экрана зависит от производительности терминала, Android-машина в конце имеет худшую производительность, и после выхода и последующего входа все еще будет этот белый экран. Мы реализовали несколько стратегий оптимизации:

1) Пакет предварительной загрузки

Когда клиент запускается, он начинает предварительно загружать пакет RN, Мы обнаружили, что после этой операции время работы белого экрана значительно сокращается, особенно для устройств Android. Но это не идеально, мы по-прежнему видим очень короткий белый экран.

2) Оптимизировать логику заставки

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

▲before
▲до

▲after
▲после

2. Сначала взаимодействие

Когда в потоке JavaScript одновременно выполняется множество действий, легко заставить поток пропускать кадры, что проявляется в зависании страницы и медленном переключении анимации.Мы можем использовать принцип «сначала взаимодействие» для оптимизации. .

1) Приоритет операций, воспринимаемых пользователем: например, переключение сцены страницы.

Например, сцена перехода страницы. Мы можем поместить логику страницы вInteractionManager.runAfterInteractionsТаким образом, сначала может быть гарантировано выполнение анимации перехода, а затем логика нашей страницы, что позволяет избежать проблемы зависания перехода.

2) Инициализируйте страницу, чтобы отобразить как можно меньше компонентов.

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

3. Оптимизация длинного списка

▲组件的子树
▲Поддерево компонента

Это поддерево компонентов. Для каждого из этих компонентов SCU указываетshouldComponentUpdateвозвращаемый контент,vDOMEqУказывает, равен ли отображаемый элемент React исходному элементу, и, наконец, цвет круга указывает, нужно ли повторно отображать компонент.

В React, если есть только один такой рендеринг поддерева компонентов, проблем с производительностью не возникает. Но если вам нужно рендерить сотни или тысячи раз для длинного списка подкачки, это будет стоить больших накладных расходов вvDOMпоколение иDiff, что напрямую приводит к серьезным проблемам с производительностью в RN для длинных списков. Итак, что нам нужно сделать, чтобы улучшить его? Давайте посмотрим на блок-схему рендеринга обновления этого компонента:

▲组件更新流程
▲Процесс обновления компонента

Когда состояние компонента или свойства меняются, он входит в функцию жизненного цикла.shouldComponentUpdateИ когдаshouldComponentUpdateЕсли он возвращает true, он позвонит метод рендера для генерацииVirtual Dom, впоследствии и старыйVirtual DomСравните и окончательно решите, стоит ли обновляться. Таким образом, мы можем ясно видеть, что SCU иVirtual DomDiff является ключом к влиянию обновлений Dom, поэтому мы оптимизировали эти две точки:

1) хороший контрольshouldComponentUpdateлогика обновления

Из приведенного выше рисунка также видно, что еслиshouldComponentUpdateЕсли возвращается false, программа может пропустить генерацию напрямую.Virtual DomА после Диффа это немалая оптимизация для сцены с большим списком, например сейчас у нас список из 1000 штук данных, когда в выпадающем списке подгружается 20 штук новых данных, еслиshouldComponentUpdateДля контроля предыдущие 1000 фрагментов данных также будут снова отображены, а вshouldComponentUpdateЕсли вы контролируете логику обновления в середине, вам нужно отображать только последние 20 элементов.Разве это не большое улучшение! Но используйтеshouldComponentUpdateБудьте особенно осторожны, вы должны учитывать всю логику, которая влияет на обновление. В противном случае он не сможет обновиться, когда он действительно нуждается в обновлении.

Давайте рассмотрим конкретный пример. Сцена — это страница со списком категорий в приложении. Мы печатаем журнал в рендере каждого элемента списка и подсчитываем количество входов в рендер. Первый взглядshouldComponentUpdateбез обработки, т.shouldComponentUpdateвсегда возвращает истину:

shouldComponentUpdate (nextProps, nextState) {
  return true
}

▲before
▲до

посмотреть, что мыshouldComponentUpdateСитуация после отсеивания ненужных элементов рендеринга с uri-адресом изображения:

shouldComponentUpdate (nextProps, nextState) {
  if (nextProps.imgSrc.uri === this.props.imgSrc.uri) {
    return false
  } else {
    return true
  }
}

▲after
▲после

Из консоли слева на рисунке видно, что независимо от того, какая страница загружается после фильтрации, отображаются только последние 20 записей, что сокращает объем ненужного рендеринга. Затем сравните время загрузки тысячи фрагментов данных при тех же условиях:

Результат тоже налицо, и в процессе эксплуатации обнаруживается, что неиспользованныеshouldComponentUpdateВ случае 1000 единиц данных время загрузки новых данных просто невыносимо.

2) При обходе массива увеличьте уникальное значение ключа

Если обновления неизбежны, вы можете найти только способы улучшитьVirtual DomДифф. эффективность. Мы можем добавить уникальное значение ключа к каждому элементу при обходе массива, чтобы на этапе Diff мы могли точно знать подкомпоненты, с которыми нужно работать, и повысить эффективность Diff.

4. Оптимизация анимации

Разумное использование анимации очень помогает улучшить работу приложения. Однако, когда мы применили анимацию, мы обнаружили, что в некоторых сценах будут лаги и выпадения кадров. Существенная причина в том, что JavaScript является однопоточным. Если в потоке выполняются какие-то тяжелые задачи, это может повлиять на производительность анимации. . . . Вот несколько способов сделать анимацию максимально естественной:

1) Используйте LayoutAnimation

Для одноразовых анимаций рекомендуется использоватьLayoutAnimation, который использует роднойCore Animation, чтобы на анимацию не влияли пропуски кадров в потоке JS и основном потоке.

2) Используйте setNativeProps

setNativePropsЭтот метод позволяет нам напрямую изменять свойства собственного компонента на основе представления без использования setState для повторного рендеринга всего дерева компонентов. Избегает больших накладных расходов на визуализацию структуры компонентов и синхронизацию слишком большого количества изменений представления.

3) Используйте родной метод драйвера

В настройках Анимационной анимации добавьтеuseNativeDriverполе и установите для него значение true, чтобы выполнение анимации могло обрабатываться изначально.

6. Публикация обновлений

В настоящее время из-за особых эффектов высокоскоростного распространения Интернета скорость разработки вещей становится все быстрее и быстрее, и особенно важна возможность быстрой итерации продукта, а также метод проб и ошибок. чтобы готовые функции быстро выводились в онлайн. Посмотрите, как мы это делаем:

1. Отпустите

Мы выбираем Jenkins в качестве решения для автоматизированного развертывания. Автоматическая упаковка достигается за счет настройки скрипта упаковки в Jenkins, и пакет RN набирается в указанное место, поэтому нет необходимости вручную упаковывать его перед каждой упаковкой, что значительно повышает эффективность.

2. Горячее обновление

Поскольку стоимость выпуска новой версии на нативной стороне относительно высока, возможность горячего обновления RN стала большой изюминкой. Просто опубликовав последний пакет на сервере, приложение в руке пользователя может автоматически загрузить удаленный пакет, а затем незаметно обновить его, что очень удобно.

После исследований мы наконец выбрали CodePush от Microsoft. Он предоставляет разработчикам RN и Cordova облачный сервис для развертывания обновлений мобильных приложений непосредственно на пользовательских устройствах, а также имеет открытый исходный код.РН версия. Подробное руководство по доступу можно посмотреть на официальном сайте, поэтому я не буду вдаваться в подробности. Вот несколько основных моментов, на которые стоит обратить внимание:

1) Зарегистрируйте приложение

Например, при регистрации приложения на CodePush необходимо различать iOS и Android.appName-iosиappName-android, его необходимо выпускать отдельно на разных платформах при выпуске.

2) Ключевая конфигурация: постановка и производство

При регистрации приложения наборdeployment key, соответственно, производственная и промежуточная среды (последующие также могут быть настроеныdeployment keyимя), в интеграцииCodePush SDKбудет использоваться, когда . Production соответствует ключу производственной среды, а Staging соответствует ключу тестовой среды. Это позволяет вам обновлять пакеты для разных сред отдельно. Если вы хотите просмотреть приложениеdeployment keyтаблицы, вы можете использовать следующую команду:

code-push deployment ls <appName> -k

3) РН доступа CodePush

Для RN ​​очень просто получить доступ к CodePush, просто добавьте несколько строк кода в корневой файл. Когда CodePush передает параметры, можно выполнять различные конфигурации в соответствии с различными средами. Код примерно такой:

import React, { Component } from 'react'
import codePush from 'react-native-code-push' // 引入 codePush
       
  const codePushOptions = __DEV__ ? {
    updateDialog: true, // 显示更新弹窗
    installMode: codePush.InstallMode.IMMEDIATE // 立即更新(会打断用户操作)
  } : {
    // 下次 app 从后台切换到前台时检查更新,并下载最新的包
    checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
    // 下次重启的时候更替换成最新的包
    installMode: codePush.InstallMode.ON_NEXT_RESTART
  }
       
  @codePush(codePushOptions)
  export default class App extends Component {
    render() {
      ...
    }
  }

checkFrequencyиinstallModeОн настраивается, и конкретная конфигурация может быть определена в соответствии с требованиями.

4) Контроль версий

При горячем обновлении необходимо контролировать номер версии.По умолчанию используется версия текущего инсталляционного пакета (трехзначный номер версии).Если вам нужно указать номер версии, вы можете добавить его при выполнении команды горячего обновления.-t, за которым следует номер версии, которую необходимо обновить.

7. Аномальный мониторинг

Мы используем платформу Tencent Bugly для мониторинга онлайн-аномалий. Платформа Bugly может предоставить разработчикам функции аномальной отчетности и оперативной статистики:

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

Например, на следующем рисунке представлена ​​статистика аварийности:

Crash также может уточнить анализ на основе таких параметров, как система, устройство и версия приложения.

Вы также можете подсчитать основные проблемы, которые больше всего влияют на пользователей:

8. Некоторые ямы и советы

За несколько месяцев разработки мы столкнулись со множеством подводных камней и нашли несколько полезных или незамеченных фишек, некоторыми из них я хочу поделиться с вами:

1. Яма

1) Потенциальная ошибка утечки памяти компонента изображения на Android

В Андроиде при загрузке картинки размер которой намного больше контейнера память резко взлетит.Если скользить по этой картинке вверх-вниз программа вылетает прямо из-за нехватки памяти.Как решить? На самом деле метод очень простой, вам нужно только установить компонент ImageresizeMethodРазмер атрибута изменяется, как показано ниже:

▲Image的resizeMethod属性说明
▲Описание атрибута resizeMethod изображения

2) ИспользованиеInteractionManager.runAfterInteractionsМеры предосторожности при

мы знаемInteractionManager.runAfterInteractionsОбратный вызов нужно выполнить после завершения анимации.Мы нашли такой баг в нашей программе.По нажатию кнопки мы не можем войти.runAfterInteractionsв обратном вызове. После расследования выяснилось, что мы выполнили бесконечный цикл анимации (эффект загрузки) и не закрыли его, поэтому мы никогда не могли войтиrunAfterInteractionsобратный вызов. Поэтому каждый должен обратить внимание на обработку круговых анимаций в разработке.

3) Проблема возврата страницы возникает при использовании списка FlatList.

FlatList имеет метод, называемыйgetItemLayoutСвойство оптимизации , если вы являетесь элементом списка с фиксированной высотой, установка этого свойства может значительно повысить эффективность рендеринга списка. Затем мы столкнулись с проблемой, заключавшейся в том, что это свойство также было установлено, когда высота была неточной, что приводило к тому, что фактическая высота в окончательном рендеринге не соответствовала нашему предустановленному значению, и произошел скачок. Итак, если вы не уверены в высоте, не устанавливайте ее.getItemLayoutАтрибуты.

▲滑动不顺畅,会发生跳动
▲Скольжение не гладкое, будут биения

▲正常滑动
▲Нормальное скольжение

4) При повторных кликах через короткий промежуток времени появляются несколько одинаковых страниц

Это не просто проблема RN, она должна быть неизбежна на всех концах. Так что обычно это фиксируется в навигационных библиотеках различных стеков, и наше первоначальное ожидание состояло в том, чтоReact NavigationВнутренне это, должно быть, было исправлено, но выяснилось, что на самом деле это не так. Так что мы правыReact NavigationПрыжок из .

▲重复跳转
▲ Повторить прыжок

▲解决后
▲После решения

function isInCurrentState (state, nextState, routeName) {
  if(nextState && nextState.routeName === routeName && !deepDiffer(state.params, nextState.params)) {
    return true
  }
  if(nextState && nextState.routes) {
    return isInCurrentState(state.routes[state.index], nextState.routes[nextState.index], routeName)
  }
  return false
}

const nextState = originGetStateForAction(action, state)

// 避免重复跳转
if (nextState && action.type === StackActions.PUSH) {
  if(isInCurrentState(state, nextState, action.routeName)) {
    return state
  }
}

2. Советы

1) Две опции в симуляторе iOS, о которых вы могли не знать

  • Откройте виртуальную клавиатуру: когда мы разрабатываем сценарии, связанные с вводом, симулятор iOS по умолчанию не открывает клавиатуру.HardWare->Keyboard->Toggle Software Keyboardвыключатель;
  • переключатель медленной анимации: С этой проблемой сталкиваются многие студенты, после того как они не знают, какая кнопка нажата, любая операция в симуляторе выполняется крайне медленно, а различные перезапуски и очистка кеша малоэффективны. Это связано с тем, что мы случайно нажали комбинацию клавиш для включения режима медленной анимации, которую можно найти вDebug->Slow Animationsзакрыть (клавиша быстрого доступаcommand+T).

2) Исходный RN и собственная связь также могут быть синхронными.

Мы знаем, что связь между RN и нативом асинхронна, но если это какие-то глобальные константы (переменные окружения, информация о версии и т.п.), то на самом деле ее можно напрямую повесить на NativeModules при синхронном запуске RN, что очень удобен в использовании.

3) Некоторые заслуживающие внимания свойства компонента Image

  • defaultSource(iOS Only): Обычно, если мы хотим реализовать функцию изображения по умолчанию, нам нужно сначала установить ссылку на изображение по умолчанию для изображения, а затем изменить состояние в обратном вызове успешной загрузки изображения, чтобы заменить изображение по умолчанию. Это свойство делает это за вас, но, к сожалению, поддерживает только iOS.
  • getSize: этот API можно использовать, когда мы хотим получить ширину и высоту изображения, а затем обработать логику, связанную с изображением.
  • prefetch: принудительное кэширование изображений.
  • queryCache: этот API может узнать, кэшировано ли изображение, и если оно кэшировано, отправлено ли оно на жесткий диск или в память. Это все еще полезно для работы с некоторой логикой кэширования, но следует отметить, что хотя официальная не отмечает этоAndroid Only, мы добились успеха только на Android, а не на iOS.

4) Некоторые заслуживающие внимания свойства в текстовом компоненте

  • allowFontScaling(iOS Only): это свойство используется для управления тем, следует ли следовать размеру системного шрифта. Если макет вашего приложения выйдет из-под контроля из-за настройки шрифта, вы можете рассмотреть возможность его включения, но это свойство поддерживает только iOS, и для решения этой проблемы Android нужны другие методы.
  • selectable: это свойство можно использовать для включения функций копирования и вставки текста.

5) Как FlatList реализует одну строку и несколько столбцов

FlatList предоставляетnumColumnsсвойства, вам нужно только установить количество столбцов в строке, вы можете легко реализовать макет одной строки и нескольких столбцов, как показано ниже:

▲一行三列的布局

▲Раскладка из одной строки и трех столбцов

6) Инструменты отладки

Рекомендуемое использованиеreact-native-debugger, который интегрирует Chrome DevTools иreact-devtools, а также поддерживает отладку, связанную с Redux, которую можно назвать очень мощной.

7) Тестирование производительности

Вы можете выполнить тестирование производительности с помощью программного обеспечения, которое поставляется с клиентом. iOS рекомендуетсяXcodeавтономныйProfile; Android рекомендуетсяAndroid StudioавтономныйAndroid Profiler.

9. Резюме

Хотя в RN все еще есть некоторые недостатки, благодаря практике проекта «Чтение энергии» результаты показывают, что RN соответствует нашим ожиданиям с точки зрения рабочей силы, производительности и эффективности. Для наилучшего применения RN в бизнес-сценариях мы также резюмируем несколько моментов:

  • Сценарий тяжелой операции: Сценарии с операционными потребностями, подходящие для реализации RN, такие как страницы книжного магазина, страницы социального обеспечения и т. д.
  • Быстро перебирать сцены: функция не идеальна, и продукт должен повторять функциональные сценарии проб и ошибок, подходящие для реализации RN, такие как круг жизненных сил, книжный магазин романов, магазин комиксов и т. д.
  • Исправлена ​​сцена отображения информации: страница отображения информации с фиксированным содержимым, подходящая для реализации RN, например, таблица лидеров, эта страница для взрослых, страница категории первого уровня и т. д.
  • сцена с длинным списком:
    • Из-за ограничения механизма рендеринга списка RN в списке картинка+длина текста много картинок неизвестного размера.Не рекомендуется реализовывать RN.Например в сценах похожих на круг витальности и WeChat круг друзей, яростный список скользящих экранов может мигать белым;
    • Большое количество длинных списков картинок известных размеров, длинных списков простого текста, да и производительность приемлемая

Независимо от того, реализована ли страница с помощью Native или RN, в дополнение к учету соотношения персонала команды на каждом конце, бизнес-сценарий также является важным фактором. Например, в новом проекте страница сведений о работе может быть реализована с помощью Native или RN для достижения цели приемки, но, учитывая, что сцена продукта на странице сведений о работе очень зрелая, и существует множество модулей, которые больше взаимодействуют с основная страница чтения, требования к опыту также особенно высоки, и мы согласны с командой терминала выбрать Native для ее достижения.

напиши в конце

Недавно команды Airbnb и Udacity заявили об отказе от RN, автор считает, что всем не стоит переживать по этому поводу. Среди регламентов, перечисленных Airbnb, многие можно оптимизировать, либо выводы нужно изучать, у других тоже есть внутренние проблемы внутри компании. Недавно команда Facebook объявила, что усердно работает над созданием крупного обновления. Упомянутые в нем направления оптимизации многопоточности, асинхронного рендеринга и мостов также заставляют нас с нетерпением ждать этого. У нас есть основания полагать, что будущее RN будет быть лучше, и мы надеемся пройти это. В этой статье рассказывается, что все больше студентов присоединились к семье RN и совместными усилиями создали лучшую экосистему RN.

Чтобы узнать больше, подпишитесь на YFE: