Почему я ушел с React Native и перешёл во Flutter: Часть 1
Всем привет. Меня зовут Дмитрий Андриянов. Два года писал на React Native, сейчас я разработчик в Surf и уже полтора года пишу на Flutter. Когда я только решил серьёзно взяться за Flutter, я бы очень хотел найти статью от разработчика, который перешёл с React Native на Flutter и узнать его мнение. Теперь этот разработчик — я.
Скорее-всего вы видели такие отзывы о React Native и как компании отказываются от него. Я поделюсь своим личным мнением со стороны одного разработчика, а не компании.
Эта статья для тех, кто
- Не знаком с кросс-платформой и хочет понять основную разницу между технологиями.
- Пишет на React Native и хочет узнать, что он выиграет при переходе на Flutter.
- Не писал на React Native, а только на Flutter.
Ниже расскажу почему в своё время я выбрал React Native, поделюсь причинами ухода во Flutter и приведу основные различия между React Native и Flutter, которые, на мой взгляд, наиболее важны. Со времени ухода с React Native прошло много времени — что-то изменилось и исправлено. Я постарался это учесть, но что-то мог упустить из виду.
Почему я выбрал React Native
В React Native я пришёл из Web-разработки на React.js в 2017 году. Компании нужен был именно React / React Native разработчик.
Причины:
- Тот же язык — JavaScript.
- Те же правила построения UI и компонентная система.
- Кросс-платформа.
Почему ушёл во Flutter
React Native хорош до тех пор, пока не начнёшь делать что-то весомое, с множеством экранов и нарастающей логикой — приложение начнёт работать всё тяжелее. Пробовал различные способы повышения производительности: shouldComponentUpdate, PureComponent, использование ключей, мемоизация, оптимизация циклов и т.д, начал задумываться о замене JSCore (Hermes на тот момент ещё не было). В итоге наткнулся на совет попробовать Flutter.
Во Flutter более богатый инструментарий, выше производительность и нет проблем React Native. Конечно, я не отрицаю, что мой код на RN мог быть не самым идеальным.
Плюсы React Native, из-за которых я начал с ним работать
1. В React Native используется удобная компонентная структура React.js.
React.js:
function App() {
return (
Edit src/App.js
and save to reload.
Learn React
);
}
function Image({logo}) {
return
}
function AppLink({children}) {
return (
{children}
);
}
export default App;
React Native:
function App() {
return (
<>
>
);
}
function Header() {
return (
head
);
}
const styles = StyleSheet.create({
scrollView: {
backgroundColor: Colors.lighter,
},
});
export default App;
2. Нет WebView и HTML — используются родные (OEM) виджеты платформы. Общение между JS и нативной частью происходит через мост.
Года два назад писал приложения с частью UI на стороне WebView. В итоге самой большой проблемой было привести его в «нативный вид», особенно отклик компонентов и прокрутку. И использование OEM виджетов автоматически избавляет от этих проблем.
3. CodePush. Позволяет практически сразу изменить код приложения у пользователей без обновления версии из магазина.
4. За время существования для React Native появилось очень много библиотек.
Один из сборников библиотек.
На моём первом React Native проекте, использовалась библиотека react-native-fcm для Push-уведомлений, требовались только уведомления и ничего больше. В другом проекте уже использовали react-native-firebase, т.к. помимо уведомлений нужна была аналитика.
Плюсы Flutter
1. Релиз.
Стабильная и надёжная версия. Одновременные релизы Dart и Flutter гарантируют, что новая версия Flutter использует самые последние наработки Dart. Так как они разрабатываются одной компанией — Google.
2. Производительность UI.
UI отрисовывается на собственном движке Skia. Во Flutter имеется свой собственный набор Material и Cupertino виджетов, которые являются копией OEM компонентов платформы. Это позволяет одновременно использовать их на обеих платформах. Больше информации о работе виджетов в статье «Flutter под капотом».
Пример запуска iOS виджета на обеих платформах:
Нет OEM компонентов и затрат на взаимодействие с ними. Хоть они и являются преимуществом React Native перед технологиями, использующими WebView, но проигрывают по гибкости и производительности виджетам Flutter. Недавно работал над приложением, использующим для Android и iOS используются в основном Material виджеты, но платформозависимые DatePicker.
Пример UI на обеих платформах:
3. Hot Reload — внедрение обновленных файлов исходного кода в работающую виртуальную машину Dart. После этого Flutter перестраивает дерево виджетов, сразу отображая изменений без перезапуска приложения. Это сильно экономит время при верстке UI и написании логики.
4. Компиляция Flutter.
В React Native только JIT-компиляция. Flutter использует JIT только в режиме разработки — это обеспечивает работу Hot Reload. В релизной сборке Flutter используется AOT-компиляция в нативные файлы платформы, что более безопасно и производительнее чем React Native, где можно в релизе получить доступ к JavaScript коду. Релизная сборка Flutter намного быстрее релизной React Native.
Сравнение производительности Native vs Flutter vs React Native.
5. Изоляты.
Во Flutter и React Native 1 процесс с 1 асинхронным потоком и тяжелые задачи заблокируют UI.
Выход из ситуации:
- Разбивать на асинхронные операции. Они не блокируют UI, но исполняются в том же потоке.
- Выносить в изолят — это независимый процесс со своим потоком, что позволяет не волноваться этой блокировке основного потока.
У Flutter Dev Podcast есть отличный выпуск про изоляты и библиотеки для работы с ними.
6. Всё является виджетом.
Благодаря этому виджеты можно вкладывать друг в друга как требуется разработчикам, без особых ограничений.
Пример: кнопка содержащая AppBar с title в виде Switch.
RaisedButton(
padding: const EdgeInsets.all(10),
child: AppBar(
title: Row(
children: [
Switch(
value: false,
),
Text('text'),
],
),
),
),
Написать свой виджет можно:
- Комбинацией виджетов.
- Используя Canvas, для более сложной геометрии.
- Используя слой рендера, когда нужно специфическое поведение.
Недавно писал функциональность, которую было проще всего сделать, используя не слой виджетов, а слой рендера с использованием RenderObject. Но об это расскажу в одной из следующих статей, где опишу проблему и её решение.
Статья про слои рендера.
Минусы React Native
1. Все ещё не 1.0.0.
Обычно в проекте оставались на определённой версии RN. Обновление часто влекло за собой поломки библиотек или внутреннего устройства RN. Инструменты автоматического обновления версии не справлялись. Быстрее было заново создать проект на новой версии, так как в аутсорсе заказчики не платят за время потраченное на обновление.
Обновлялись только когда этого требовала необходимая библиотека или в новой версии был исправлен критический для нас баг. В этом случае был риск, что старые библиотеки могли быть несовместимы с новой версией React Native. Но уже прошло полтора года, проверим как с этим сейчас. Попробуем обновить React Native за пару минут, ведь нам важно время заказчика.
Создаём проект React Native на v0.61.5 (предпоследняя на данный момент).
react-native init test_version --version 0.61
Запускаем:
Всё в порядке.
Инициализируем репозиторий, так как upgrade работает поверх Git и без него будет ошибка:
Запускаем npx react-native upgrade:
Чистый проект при обновлении выдаёт ошибку.
Пройдём по ссылке предлагаемой в треминале. Список файлов, которые нужно будет обновить вручную:
- package.json
- .gitattributes
- .flowconfig
- App.js
- app.js/android/app/build.gradle
- android/app/src/debug/java/com/rndiffapp/ReactNativeFlipper.java
- android/app/src/main/AndroidManifest.xml
- android/app/src/main/java/com/rndiffapp/MainApplication.java
- android/build.gradle
- android/gradle.properties
- android/gradle/wrapper/gradle-wrapper.properties
- android/gradlew
- ios/Podfile
- ios/RnDiffApp.xcodeproj/xcshareddata/xcschemes/RnDiffApp-tvOS.xcscheme
- ios/RnDiffApp.xcodeproj/xcshareddata/xcschemes/RnDiffApp.xcscheme
- ios/RnDiffApp/AppDelegate.m
Обновив файлы, запускаю upgrade:
Запускаем npm install. Готово. Проект обновился.
Главный критерий — время. Оно тратится на обновление и исправление проблем, либо на пересоздание проекта с нуля. На Flutter же пишем в консоле «flutter upgrade» и готово. Flutter обновится для всех проектов сразу. Могут быть ломающие изменения, но обычно их пара штук на весь проект и они очень просто чинятся.
2. Производительность.
Её оказалось недостаточно. Всё ещё не так быстро, как хотелось бы. Тестировщики постоянно ставили в баги низкую «не нативную» производительность. Не всегда спасали оптимизации. С ростом приложения больше данных сериализуется для моста и тем меньше отзывчивость.
3. Так как используются нативные компоненты, то на Android и iOS они выглядят по-разному.
Чтобы достичь единообразия придётся писать аналог компонента для одной из платформ или с нуля для обеих. Либо проектировать дизайн с учетом этих особенностей и различий.
Один и тот же UI. Слева iOS, справа Android.
Дополнительно
JavaScriptCore
В React Native долгое время был старый JS Core для Android. С появлением Hermes ситуация улучшилась, но судя по отзывам знакомых разработчиков и тестам — всё не так однозначно.
Сравнение JS движков.
Expo
Его сложно отнести к плюсам и минусам. Он предоставляет из коробки важные библиотеки и функциональность, но у него есть свои проблемы, нивелирующие его плюсы. Если нужна какая-то отсутствующая в Expo функциональность на уровне платформы или внешняя библиотека, использующая нативный код, то придется выталкивать проект и плюсы Expo либо исчезают, либо превращаются в его минусы. React Native init vs Expo.
Минусы Flutter
Несправедливо было бы их не отметить. Всё же, это не серебряная пуля.
1. Меньше специалистов и вакансий.
Меньше разработчиков и компаний, разрабатывающих на Flutter, в сравнении с React Native. Когда я в мае 2019 года искал работу на Flutter, мне попалось всего 3 компании.
2. Количество библиотек меньше, чем в React Native.
Из-за того, что Flutter более молодая технология, то и библиотек для неё куда меньше, но их количество активно растет. Но за год с лишним работы это не доставило мне особых проблем, так как есть все нужные библиотеки.
3. Не подходит для:
- Игр.
- Приложений, завязанных на работе с железом.
- Приложения с дополненная реальностью. Так как нужно реализовывать логику отдельно для каждой платформы.
Но если много общего UI, возможно, подойдет вариант использования каналов платформы для взаимодействия с нативным кодом или интегрировать Flutter в нативное приложение.
Документация
В React Native достаточно хорошая документация, которая отвечает на многие возникающие вопросы. Она постепенно улучшается (раньше можно было наткнуться на пустые страницы: заголовок есть, а контента нет) и материал становится лучше.
Во Flutter же документация шикарна. Примеры, объяснения, рецепты с примерами, видео. Если есть проблема с использованием чего-либо — документация способна её решить.
Порог вхождения
В обоих случаях порог вхождения довольно низкий.
- React Native — нужно знать JS, React и можно начинать.
- Flutter — если вы знаете JS / Java / Kotlin / Swift / C (а если JS и любой из остальных — это вообще идеальный вариант), Dart дастся очень легко.
А если есть ещё и опыт на React Native, то декларативная верстка Flutter не вызовет сложностей.
Итог
React Native
- Больше библиотек.
- Больше разработчиков.
- Проблемы с производительностью.
- Не безопасен.
- Низкий порог вхождения.
Flutter
- Библиотек меньше, но их число растёт. А необходимые есть.
- Разработчиков меньше, но переучиться несложно.
- Производительность.
- Безопасен.
- Низкий порог вхождения.
Пользователю неважно, что вы используете для сетевых запросов Http или Dio.
Искали вы JS-разработчика день или Flutter-разработчика месяц. Главное, чтобы приложение выполняло требуемые задачи и было производительным. Flutter позволяет отвечать этим требованиям гораздо эффективнее.
Разбор решения одних и тех же задач используя Flutter и React Native будет во второй части статьи.