React Native vs Flutter. Обзор архитектур и «под капотом»

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Всем привет, меня зовут Алексей, я Lead React/React Native разработчик в Customertimes и вот уже 10 лет занимаюсь веб (фронтенд и бэкенд) и мобайл (нативной и кросс-платформенной) разработкой.

В этой статье я хочу рассказать о двух популярных кросс-платформенных решениях для мобильной разработки — React Native и Flutter. Это первая часть, в ней будет мало погружения в отдельные API, я остановлюсь на обзоре архитектур, том, что под капотом, и поверхностном сравнении.

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

Архитектура React Native

Архитектура React Native. Источник

React Native появился в 2015 году как продолжение React.js для мобильных устройств. Изначально этот фреймворк был только iOS, из-за чего долгое время iOS был немного лучше оптимизирован. React Native — это open-source разработка компании Facebook. Основная идея этого решения — отрисовка UI нативными элементами платформ, а также управление и бизнес-логика на стороне JS.

Немного детальнее о архитектуре: в основе лежит взаимодействие JS с нативным UI и API с помощью моста (bridge). JS часть работает на архитектуре React, что делает разработку React.js и React Native очень схожей. Основной минус, что все данные проходят конвертацию. Также для построения интерфейсов используется библиотека Yoga — разработка Facebook, которая упрощает взаимодействие с UI. С версии 0.60 RN начал использовать свой JS engine (Hermes) для Android, с 0.64 также для iOS. С 2018 года команда React Native работает над новой архитектурой, которая уберет прослойку-мост, но по состоянию на апрель 2021 года нет даты релиза (ссылка на обсуждение).

Для установки JS библиотек используется npm/yarn пакетные менеджеры. Могут быть использованы как специальные библиотеки для React Native, так и библиотеки для node.js, например утилита для работы с base64. Для нативных библиотек используется Cocoapods и Gradle. При установке библиотек с нативными зависимостями необходимо устанавливать нативные пакетные менеджеры, ранее также требовалась линковка нативных библиотек.

При компиляции Metro bundler для релиз-режима делает jsbundle со всем js кодом, который исполняется при запуске приложения в JS машине. JS использует JIT компиляцию.

Архитектура Flutter

Архитектура Flutter. Источник

Flutter — это open-source разработка компании Google, которая вышла в свет в 2017 году. На данный момент уже состоялся релиз второй версии. Они реализовали кросс-платформенность немного другим путем, с помощью эмуляции нативных элементов в канвасе — схожий подход используют игровые движки. Вся UI отрисовка выполняется библиотекой Skia.

Архитектуру Flutter можно разделить на 3 слоя:

  • Embedder — часть, которая отвечает за интеграцию с ОС, например, для Android и Windows будут разные embedder.
  • Engine — в ней реализованы основные API, которые отвечают за рендеринг, сетевые и файловые I/O, Dart runtime.
  • Flutter Framework использует Dart в качестве языка программирования, а Framework имеет набор служебных функций, с помощью которых можно добавлять элементы пользовательского интерфейса и виджеты в свое приложение Flutter.

Dart — это строго типизированный объектно-ориентированный язык программирования. И у него есть особенности досрочной компиляции и компиляции точно в срок. Flutter поддерживает JIT и AOT компиляцию. JIT — только для функционала Hot Reload.

Dart так же однопоточен, как и node.js. Для реализации асинхронности используется механизм Event Loop. Подробнее об устройстве Dart Runtime во Flutter можно узнать в подкасте.

Досрочная компиляция делает Flutter SDK и Dart подходящими для генерации собственного кода ARM, который может быть скомпилирован на Android и iOS.

Для установки библиотек используется Pub менеджер пакетов.

Сравнение архитектур нативных, React Native и Flutter приложений

Архитектура нативного приложения. Источник

Архитектура React Native приложения. Источник

Архитектура Flutter приложения. Источник

Ключевое различие — это отсутствие OEM Widgets (original equipment manufacturer widgets) в Flutter архитектуре, что дает возможность добиваться 60 fps.

Кто использует React Native и Flutter

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

Известные приложение React Native

Источник: reactnative.dev/showcase

Известные приложение Flutter

Источник: flutter.dev/showcase

Сравнение языков Javascript vs Dart

Интро

Javascript — один из самых популярных языков разработки на данный момент, на нем сейчас пишут веб, серверные, десктопные и мобильные приложения.

Dart пока что не настолько популярен, но компания Google начала его разрабатывать как улучшение/противовес Javascript, поэтому использовала лучшие наработки из разных языков программирования.

Пакетные менеджеры

Для Javascript основной пакетный менеджер — это npm, альтернатива — yarn (более 1,3 миллиона библиотек).

Для Dart существует свой пакетный менеджер — pub (17 357 библиотек на данный момент). Комьюнити активно увеличивает количество библиотек ежедневно, но стоит обратить внимание, что для решения узких задач может не быть подходящей библиотеки для Dart.

Популярность JS делает очень распространенными библиотеки для решения различных задач, но огромный минус, что из этих 1,3 млн библиотек в NPM не понятно, какие поддерживаются. С React Native библиотеками та же проблема. Часто непопулярные, узко специализированные библиотеки не поддерживаются более трех лет. С Flutter из-за «свежести» решения пока такой проблемы нет.

Изучение

Javascript одновременно прост и сложен в понимании. Начать писать код можно очень быстро: есть много онлайн-песочниц для исполнения кода, можно использовать ваш браузер (dev консоль), можно начать с экспериментов с DOM деревом на любом сайте и т.д. Существует огромное количество курсов по JS, а также вебинаров/YouTube-каналов, где можно найти уроки любой сложности — от Trainee до Senior разработчика.

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

Скорость

Можно посмотреть сравнение скорости Dart vs Node.js здесь.

JS имеет JIT компиляцию, Dart — JIT & AOT компиляцию, что ускоряет процесс разработки (JIT) и компилирует более оптимизированный код (AOT).

Среды применения

Dart получил популярность благодаря появлению Flutter, который является фреймворком «напиши один раз и используй везде». На деле не без проблем, но Flutter поддерживает iOS, Android, Web (да, если вы еще не видели сайтов с UI на Canvas API — прошу в демки Flutter), а также десктоп (MacOS, Windows, Linux). Но, помимо этого, в будущем возможно более широкое применение как на серверах, так и на клиентах. На данный момент в документации Dart есть примеры, как можно использовать для рендеринга html на сервере.

Javascript имеет огромнейший набор фреймворков и библиотек. Он возник как язык для скриптинга экшенов на сайтах, но перерос в полноценный инструмент с появлением Node.js, который позволяет использовать JS для бэкенда. Для веб-приложений существует много фреймворков/библиотек. Самые популярные из них — React.js, Angular, Vue.js, для десктопов — Electron и его альтернативы, для мобайл — Ionic, Cordova, React Native, NativeScript.

Основные различия синтаксиса

Точка входа

В JS точкой входа может быть любой вызов функции. В случае RN это будет AppRegistry:

// JavaScript
function startHere() {
  // Can be used as entry point
}

В Dart каждое приложение начинается с main:

// Dart
main() {
}

Вывод консоли

// JavaScript
console.log('Hello world!');
// Dart
print('Hello world!');

Переменные

В Dart строго типизированный язык:

// JavaScript
var name = 'JavaScript';
// Dart
String name = 'dart'; // Explicitly typed as a string.
var otherName = 'Dart'; // Inferred string.
// Both are acceptable in Dart.

Значение по умолчанию:

// JavaScript
var name; // == undefined
 
// Dart
var name; // == null
int x; // == null

Больше о переменных и типизации.

Проверка null и zero

// JavaScript
var myNull = null;
if (!myNull) {
  console.log('null is treated as false');
}
var zero = 0;
if (!zero) {
  console.log('0 is treated as false');
}
 
// Dart
var myNull = null;
if (myNull == null) {
  print('use "== null" to check null');
}
var zero = 0;
if (zero == 0) {
  print('use "== 0" to check zero');
}

Функции

// JavaScript
function fn() {
  return true;
}
 
// Dart
fn() {
  return true;
}
// can also be written as
bool fn() {
  return true;
}

Промисы

// JavaScript
class Example {
  _getIPAddress() {
    const url = 'https://httpbin.org/ip';
    return fetch(url)
      .then(response => response.json())
      .then(responseJson => {
        const ip = responseJson.origin;
        return ip;
      });
  }
}

function main() {
  const example = new Example();
  example
    ._getIPAddress()
    .then(ip => console.log(ip))
    .catch(error => console.error(error));
}

main();

 
// Dart
import 'dart:convert';
import 'package:http/http.dart' as http;

class Example {
  Future<String> _getIPAddress() {
    final url = 'https://httpbin.org/ip';
    return http.get(url).then((response) {
      String ip = jsonDecode(response.body)['origin'];
      return ip;
    });
  }
}

main() {
  final example = new Example();
  example
      ._getIPAddress()
      .then((ip) => print(ip))
      .catchError((error) => print(error));
}

Async/Await

// JavaScript
class Example {
  async function _getIPAddress() {
    const url = 'https://httpbin.org/ip';
    const response = await fetch(url);
    const json = await response.json();
    const data = json.origin;
    return data;
  }
}

async function main() {
  const example = new Example();
  try {
    const ip = await example._getIPAddress();
    console.log(ip);
  } catch (error) {
    console.error(error);
  }
}

main();
 
// Dart
import 'dart:convert';
import 'package:http/http.dart' as http;

class Example {
  Future<String> _getIPAddress() async {
    final url = 'https://httpbin.org/ip';
    final response = await http.get(url);
    String ip = jsonDecode(response.body)['origin'];
    return ip;
  }
}

main() async {
  final example = new Example();
  try {
    final ip = await example._getIPAddress();
    print(ip);
  } catch (error) {
    print(error);
  }
}

Как видим, базовый синтаксис очень похож, но не стоит забывать о типизации, о немного другом синтаксисе. Dart требует глубокого изучения, особенно после JS, для понимание работы, ведь Dart ближе к Java с OOP подходом, чем к JS c функциональным программированием.

Доступные API «из коробки»

React Native

Изначально React Native включал больше API, которые позже вышли в отдельные библиотеки, такие как Webview, AsyncStorage и т.д. Это было сделано для упрощения поддержки библиотек и ядра фреймворка. «Из коробки» у React Native нет средств для отрисовки канвас элементов, работы с файлами, файловым хранилищем. Этот функционал становится доступен после установки библиотек. При создании проекта вы получите две библиотеки — react и react-native. React предоставляет все API для работы с компонентами, контекстом, хуками и рефами.

Из коробки React Native предоставляет 24 компонента и 31 API. Узнать детальнее о каждом компоненте или API вы можете здесь.

В списке отсутствуют встроенные механизмы для работы с сетью, fetch (web fetch API), xhr и websocket, а также API для работы с формами FormData, которое используется в некоторых проектах для отправки файлов на сервер. Набор API и компонентов покрывает основные потребности для построения базового UI любого приложения.

Flutter

В React мире всё является компонентами. У Flutter схожий подход, но только компоненты называют виджетами. Также из коробки Flutter дает две визуальные библиотеки: Material и Cupertino. Детальнее о виджетах с категоризацией вы можете найти здесь.

Из коробки Flutter поставляет библиотеки для работы с графикой, который можно найти здесь, большая часть из которых это и есть виджеты, описанные выше:

  • Animation
  • Cupertino
  • Foundation
  • Gestures
  • Material
  • Painting
  • Physics
  • Rendering
  • Scheduler
  • Semantics
  • Services
  • Widgets
  • Dart:io

Итоги о наборе базового функционала

React Native «из коробки» не предоставляет решения для навигации или работы со state менеджментом (если вы не предпочитаете контекст), а также доступ к API платформе. В то же время Flutter дает даже слишком много для создания и контроля UI с помощью их 2D engine, но при этом без установки библиотек нет возможности работать с дополнительными API платформ (Storage, Camera, GEO и другие). Например, для нетворк запросов нужна установка дополнительной библиотеки (http). По UI Flutter c анимациями в 60 fps и полным контролем 2d рендеринга лидирует над React Native.

Инструменты разработчика

IDE

React Native

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

  • Visual Studio Code
  • WebStorm (платный)
  • Sublime Text и другие.

Основное, на что стоит обратить внимание, это поддержка синтаксиса RN и сниппетов.

Flutter

Вы также можете использовать любой редактор, но линтинг смогут поддерживать не все, поэтому список из Flutter документации:

  • Android Studio / IntelliJ IDEA
  • Visual Studio Code (dart plugin)
  • Emacs (dart package)

Что нужно для разработки

React Native

Для работы с React Native нужна одна из систем MacOS, Windows, Linux. Для запуска на девайсах вам потребуется MacOS, которая вам позволит работать и с iOS, и с Android. Также для React Native есть отличный облачный инструмент Expo, который позволяет создавать React Native приложения, используя только JS. Единственные минусы облачного решения — платность и привязка к ограниченным библиотекам сервиса.

Для локального развертывания проекта вам потребуется:

  • Node.js
  • NPM или Yarn
  • Xcode, Cocoapods
  • Android Studio (с Android SDK, JDK)
  • Сам React Native устанавливается как библиотека npm c зависимостями iOS и Android

Flutter

С Flutter та же ситуация. Разрабатывать вы можете на любой системе, но для запуска вам потребуется MacOS. Аналога Expo на данный момент для Flutter нет.

Для локального развертывания проекта вам потребуется:

  • Flutter SDK
  • Xcode, Cocoapods
  • Android Studio (с Android SDK, JDK)

DevTools

Оба фреймворка поддерживают Fast Refresh (Hot Reload) в режиме дебаггинга. Это означает, что вам не нужно билдить приложение каждый раз, когда вы меняете что-то в приложении.

React Native

Flutter

  • Flutter DevTools (сразу доступен в вашей IDE, если ставите расширение)

CI/CD

Оба фреймворка можно билдить в облаке, здесь всё зависит от сервисов для облачных CI/CD, либо использовать инструменты для автоматизации наподобие fastlane.

React Native

  • Appcenter
  • Bitrise
  • Codemagic
  • CircleCI
  • GitHub Action / GitLab CI / Bitbucket Pipelines
  • Azure DevOps
  • Fastlane
  • Travis
  • и другие.

Flutter

  • Codemagic
  • Bitrise
  • AppCircle
  • Fastlane
  • Travis
  • GitHub Action/GitLab CI.

Production debugging

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

React Native

  • Sentry
  • Firebase Crashlytics
  • Instabug
  • Bugsnag
  • Rollbar

Flutter

  • Sentry
  • Firebase Crashlytics
  • Instabug
  • Bugsnag
  • Rollbar

Зарплаты разработчиков

Графики с djinni.co по Украине

Графики с ziprecruiter.com по США

Распространенность кросс-платформенных технологий

Statista

Как видно из графика, Flutter активно набирает популярность, и в 2021 году может догнать по популярности React Native.

Upwork

Как еще один маркер популярности, можно взять количество заказов на Upwork, для React Native — 6500 заказов, для Flutter — 1263 заказа.

Сравнение на практике (UI)

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

Исходный макетReact Native (iOS)React Native (Android)



Исходный макетFlutter (iOS)Flutter (Android)



Live React Native


React Native

Исходные файлы

Использовано

  • React Native API (StyleSheet, Text, View, FlatList, TouchableOpacity, Platform, StatusBar)
  • react-native-androw (библиотека для кастомизации теней элементов)
  • react-native-vg (библиотека для работы с рисованием векторных элементов, все иконки — это path, и график — это тоже динамически генерируемые path)
  • d3-shape (библиотека для генерации фигур графика)
  • кастомные шрифт из мокапа Avenir Next

Ограничения

  • Работа с тенями, iOS — полная стилизация, Android — только в системе дизайна Material
  • Кастомная графика (векторы, canvas, градиенты) — ничего нет «из коробки», только с помощью растровой графики (png, jpg), если хотите что-то рисовать — используйте библиотеки наподобие react-native-svg.

Flutter

Исходные файлы

Использовано

  • Flutter API
  • flutter_svg (библиотека для работы с рисованием векторных элементов)
  • touchable_opacity (библиотека-обертка, которая упростила создание кликабельных элементов)
  • fl_chart (библиотека для построения графиков, стоит обратить внимание, что она не позволила полностью повторить график из макета, для этого нужно рисовать график с нуля)

Итоги

На мой взгляд, Flutter имеет огромный потенциал из-за архитектурного подхода для отрисовки UI. Вы получаете инструмент, похожий на Unity, в канвасе которого есть возможность рисовать всё что угодно: любую графику, любые эффекты и анимации.

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

Если разобраться, скорость построения интерфейсов идентична у RN и Flutter. Из минусов — это сложность найти разработчиков. Рынок JS разработчиков огромен, проще JS разработчика обучить тонкостям React Native и мобильной разработки, чем обучить человека Dart. Ещё один минус — это количество библиотек. Тогда как в мире JS существуют тонны библиотек под любые нужды, здесь пока их немного, но под основные задачи достаточно. Но при этом не составит труда переписать небольшую библиотеку с JS на Dart.

Если вы много лет работаете с JS и начали смотреть в сторону Dart — ещё рано переходить, вы сейчас работаете в среде самого огромного комьюнити. Но стоит присмотреться. Если вы новичок — Flutter отличная ниша для начала карьеры в IT.

👍НравитсяПонравилось21
В избранноеВ избранном12
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

То что говорят что Flatter умет в web — это полный бред, посмотрите на эти демки на канвасе... это же смешно, плюс нет ssr, кому это нужно без seo? Пожалуйста не надо пытаться сделать одно приложение везде! Есть понятие мобильное приложение (Native, Flutter/React и тд), веб (Angular/React/Vue и тд), пусть каждая технология делает свою работу 😎

Отличное сравнение!

Факты, данные, без субъективных суждений. Перекликается с нашим опытом использования.

Не очень понятно, на основании чего сделан список CI/CD — я, допустим, билдил флаттер в Azure DevOps, но Azure DevOps для флаттера не указан.

Питання трохи в бік від теми.
Є андроід додаток написаний на Kotlin
Який найпростіший спосіб конвертувати цей додаток у кросплатформ?
Чи буде просто конвертувати у KMM ? Чи то лише для додатків написаних з нуля?
Якось можна використовувати код котлін у додатках на react native, flutter?

KMM чисто для бизнес логики, UI нужно будет писать отдельно для обеих платформ.

У Котліна є експериментальна підтримка мобільного кросплатформу, про яку ви написали.
Це буде найкраще рішення щодо «використання наявного коду», для цього необхідно буде розділити код на те що під iOS, те що під Android, і те що спільне.
Перевикористати код у flutter та react native є малоймовірно.
Проте міграція на ці фреймворки не повинна забрати багато часу, особливо при Flutter+GetX.
На мою особисту думку, простіше буде створити окремий Swift додаток під iOS, якщо Вам лише для смартфонів.
А на той же Flutter дивитися якщо вам важливе перенесення під desktop платформи чи web у майбутньому.

Рынок JS разработчиковJS разработчиков огромен, проще JS разработчика обучить тонкостям React Native и мобильной разработки, чем обучить человека Dart

Чему там обучать? Они почти не отличаются

React Native досить легко описати. Його розробляє Facebook.
Ви колись користувалися мобільним додатком від Facebook?
Бачили як він лагає?
Flutter розробляє Google.
Ви колись бачили лаги в додатках Google?

Ви колись бачили лаги в додатках Google?

maps лагає постійно.

В смысле? Я использую android auto, когда телефон транслирует на экран в машине свои приложения, ни разу не видел, чтобы google maps лагала.

Flutter — це безодня неперевершеності

аксиома Эскобара

да можно было просто написать что флаттер лучше

чем? оба фрейма так-себе, через пару лет KMM превзойдет их
п.с я на РН пишу

п.с я на РН пишу

Соболезную)

Обзор хороший.Я бы добавил для флаттера это уже сформировавшийся огромный зоопарк state management control систем.Это не видно когда начинаешь работать, но это становится огромной болью в больших проектах.Вникать во всю эту лапшу прийдется,не так там все радужно.

На відміну від Реакту? Не знаю що там вигадали у флаттері, але мені доводилося працювати з реакт проектом, де був дикий мікс контексту, редаксу і пропси на 2 екрани. Невже флаттер це може перевершити?

Да, можно, особенно на больших немецких проектах.Это не видно если Вы автор такой лапши.Но когда прийдется разбираться в чужом коде — это квест!

Подписаться на комментарии