Главная » Хабрахабр » [Перевод] Руководство по разработке Web-приложений на React Native

[Перевод] Руководство по разработке Web-приложений на React Native

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

image

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

Зачем это всё?

Аббревиатура «PWA» (Progressive Web Apps, прогрессивные веб-приложения) сегодня у всех на слуху, этот трёхбуквенный акроним кажется прямо-таки китом в море технических терминов. Но и эта популярная технология всё ещё не лишена недостатков. С такими приложениями связано немало технологических сложностей, существуют ситуации, в которых разработчик вынужден параллельно создавать нативные и веб-приложения. Вот хорошая статья, в которой сравниваются PWA и нативные приложения.

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

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


Papu — приложение для заказа еды, предназначенное для Android, iOS и для веба

Базовые строительные блоки приложения

Здесь мы пользуемся React, поэтому нам надо отделить логику приложения от пользовательского интерфейса. Тут лучше всего использовать какую-нибудь систему для управления состоянием приложения вроде Redux или Mobx. Подобный ход сразу же делает логику функционирования приложения универсальной. Её, без изменений, можно использовать на разных платформах.

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

Например, стандартный веб-контейнер обратится к вам так:

<div>Здравия желаю! Стандартный веб-контейнер к вашим услугам!</div>

А нативный — так:

<View>Привет! Я - простой контейнер в React Native</View>

Некоторые умные люди нашли выход из этой ситуации. Выходом стали специализированные библиотеки элементов. Одна из моих любимых — это замечательная библиотека React Native Web. Она не только берёт на себя заботу о базовых примитивах приложения, позволяя использовать компоненты React Native в вебе (не все компоненты!), но и даёт доступ к различным API React Native. Среди них — Geolocation, Platform, Animated, AsyncStorage и многие другие. Взгляните на замечательные примеры, которые можно найти в руководстве к этой библиотеке.

Шаблон

С примитивами мы разобрались. Но нам ещё нужно связать среды для веб-разработки и для нативной разработки. В моём проекте использованы create-react-app (для веб-приложения) и скрипт инициализации React Native (для нативного приложения, без Expo). Сначала я создал один проект такой командой: create-react-app rnw_web. Затем создал второй проект: react-native init raw_native. Затем я, последовав примеру Виктора Франкенштейна, взял файлы package.json из этих двух проектов и объединил их. После этого я, в папке нового проекта, скормил новый файл yarn. Вот о каком package-файле идёт речь:

, "devDependencies": { "babel-jest": "^23.4.0", "babel-preset-react-native": "^5", "jest": "^23.4.1", "react-scripts": "1.1.5", "react-test-renderer": "^16.3.1" }, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start", "test": "jest", "start-ios": "react-native run-ios", "start-web": "react-scripts start", "build": "react-scripts build", "test-web": "react-scripts test --env=jsdom", "eject-web": "react-scripts eject" }
}

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


Папки, которые нужно скопировать в новый проект

Благодаря webpack мы можем использовать расширения имён файлов для того, чтобы сообщить бандлеру о том, где какие файлы нужно использовать. Теперь, в папке src, которая лежит в директории нового проекта, создадим два файла: App.js и App.native.js. Разделить App-файлы жизненно важно, так как мы собираемся использовать разные подходы для навигации по приложениям.

Для навигации используется react-router. Вот файл App.js, предназначенный для веба.

// App.js - WEB
import React, { Component } from "react";
import { View } from "react-native";
import WebRoutesGenerator from "./NativeWebRouteWrapper/index";
import { ModalContainer } from "react-router-modal";
import HomeScreen from "./HomeScreen";
import TopNav from "./TopNav";
import SecondScreen from "./SecondScreen";
import UserScreen from "./UserScreen";
import DasModalScreen from "./DasModalScreen"; const routeMap = { Home: { component: HomeScreen, path: "/", exact: true }, Second: { component: SecondScreen, path: "/second" }, User: { component: UserScreen, path: "/user/:name?", exact: true }, DasModal: { component: DasModalScreen, path: "*/dasmodal", modal: true }
}; class App extends Component { render() { return ( <View> <TopNav /> {WebRoutesGenerator({ routeMap })} <ModalContainer /> </View> ); }
} export default App;

Вот App.js для React Native-приложения. Тут для навигации используется react-navigation.

// App.js - React Native import React, { Component } from "react";
import { createStackNavigator, createBottomTabNavigator
} from "react-navigation";
import HomeScreen from "./HomeScreen";
import DasModalScreen from "./DasModalScreen";
import SecondScreen from "./SecondScreen";
import UserScreen from "./UserScreen"; const HomeStack = createStackNavigator({ Home: { screen: HomeScreen, navigationOptions: { title: "Home" } }
}); const SecondStack = createStackNavigator({ Second: { screen: SecondScreen, navigationOptions: { title: "Second" } }, User: { screen: UserScreen, navigationOptions: { title: "User" } }
}); const TabNav = createBottomTabNavigator({ Home: HomeStack, SecondStack: SecondStack
}); const RootStack = createStackNavigator( { Main: TabNav, DasModal: DasModalScreen }, { mode: "modal", headerMode: "none" }
); class App extends Component { render() { return <RootStack />; }
} export default App;

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

Сейчас мы немного этот шаблон усложним, добавим систему маршрутизации/навигации.

Проблемы навигации и их решения

Приложению, если только оно не состоит из одного экрана, нужна некая система навигации. Сейчас (речь идёт о сентябре 2018 года) существует лишь одна такая универсальная рабочая система, подходящая и для веба, и для нативных приложений. Речь идёт о React Router. Для веба это решение подходит хорошо, а вот в случае с React Native-проектами всё уже не так однозначно.

Другие средства навигации, вроде React Navigation, этими возможностями обладают. В React Router Native нет переходов между экранами, нет поддержки кнопки Назад (для платформы Android), нет модальных экранов, навигационных панелей и других возможностей.

Поэтому в моём проекте за навигацию в веб-приложении отвечает React Router, а за навигацию в нативном приложении — React Navigation. Я использовал именно эту библиотеку, но вы можете подобрать что-нибудь другое. Дело в том, что подходы к навигации и к передаче параметров в этих системах очень сильно различаются. Это, правда, создаёт новую проблему.

Это дало мне возможность создать API, напоминающее React Navigation. Для того чтобы сохранить дух React Native Web, используя везде подход, близкий к тому, что применяется в нативных приложениях, я подошёл к решению этой проблемы, создавая веб-маршруты и оборачивая их в HOC.

Такой подход позволил перемещаться между экранами веб-приложения, избавив меня от необходимости создавать отдельные компоненты для двух видов приложения.

Первый шаг реализации этого механизма заключается в создании объекта с описанием маршрутов для веб-приложения:

import WebRoutesGenerator from "./NativeWebRouteWrapper"; // функция собственной разработки, которая генерирует маршруты React Router и оборачивает их в HOC
const routeMap = { Home: { screen: HomeScreen, path: '/', exact: true }, Menu: { screen: MenuScreen, path: '/menu/sectionIndex?' }
}
//в методе render
<View> {WebRoutesGenerator({ routeMap })}
</View>

В сущности, тут представлена копия функции создания навигатора React Navigation с добавлениями специфических для React Router возможностей.

Это позволяет клонировать компонент screen и добавить navigation в его свойства. Далее, пользуясь моей вспомогательной функцией, я создаю маршруты react-router и оборачиваю их в HOC. Этот подход имитирует поведение React Navigation и делает доступными методы вроде navigate(), goBack(), getParam().

Модальные экраны

React Navigation, благодаря createStackNavigator, даёт возможность сделать так, чтобы некая страница приложения выезжала бы снизу в виде модального экрана. Для того чтобы добиться подобного в веб-приложении, мне пришлось использовать библиотеку React Router Modal. Для работы с модальным экраном сначала надо добавить соответствующую опцию в объект routeMap:

const routeMap = { Modal: { screen: ModalScreen, path: '*/modal', modal: true //маршрутизатор будет использовать компонент ModalRoute для рендеринга этого маршрута }
}

Кроме того, в макет приложения нужно добавить компонент <ModalContainer /> из библиотеки react-router-modal. Выводиться соответствующая страница будет именно там.

Навигация между экранами

Благодаря HOC нашей разработки (временно этот компонент называется NativeWebRouteWrapper , и это, кстати, ужасное имя), мы можем использовать практически тот же набор функций, что и в React Navigation, для организации перемещения между страницами в веб-версии приложения:

const { product, navigation } = this.props
<Button onPress={navigation.navigate('ProductScreen', {id: product.id})} title={`Go to ${product.name}`}
/>
<Button onPress={navigation.goBack} title="Go Back"
/>

Возврат к предыдущему экрану

В React Navigation можно вернуться назад на n экранов, которые находятся в навигационном стеке. Подобное в React Router недоступно, тут нет навигационного стека. Для того чтобы решить эту проблему, нам нужно импортировать в код функцию pop собственной разработки. Вызывая её, передадим ей несколько параметров:

import pop from '/NativeWebRouteWrapper/pop'
render() { const { navigation } = this.props return ( <Button onPress={pop({screen: 'FirstScreen', n: 2, navigation})} title="Go back two screens" /> )
}

Опишем эти параметры:

  • screen — имя экрана (используемое React Router в веб-версии приложения).
  • n — число экранов для возврата с использованием стека (используется React Navigation).
  • navigation — объект, обеспечивающий навигацию.

Результаты работы

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

Первый представляет собой чистую универсальную среду для разработки веб-приложений и нативных приложений.

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

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

Итоги

Сообществу React-разработчиков, безусловно, нужна универсальная библиотека для организации навигации по приложениям, так как это упростит разработку проектов, подобных тому, о котором мы говорили. Очень хорошо было бы, если бы библиотека React Navigation работала бы и в вебе (на самом деле, подобная возможность уже доступна, но работа с ней не лишена сложностей).

Уважаемые читатели! Пользуетесь ли вы возможностями React при создании универсальных приложений?


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Получаем музыку Вк через сторонний API

В этот раз дело начиналось после закрытия методов audio в методе execute. Я решил посмотреть, как получают музыку сайты, которые предоставляют возможность ее скачать. Меня заинтересовал сайт vrit.me. Я залез во вкладку network и увидел интересный запрос: То есть, можно ...

Все, что нужно знать о стрессе и сильных эмоциях

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