Холодное погружение в React Native (учебник для начинающих)

Опубликовано: 2022-03-11

Когда был анонсирован React Native, первая реакция была исключительно положительной. Традиционно, когда мы думаем о веб-технологиях в мобильном пространстве, на ум приходят такие вещи, как Apache Cordova, которые позволяют нам упаковывать веб-сайты или веб-приложения в виде приложений для мобильных платформ. В этом руководстве для начинающих мы рассмотрим архитектуру React Native, философию React Native и ее отличия от других решений в той же области. К концу статьи мы превратим приложение React «Hello World» в приложение React Native.

Начнем с того, что React Native — относительно новая технология. Он был официально доступен с марта 2015 года, с начала того же года находился в закрытой бета-версии, а до этого некоторое время использовался внутри Facebook. Поговорка «Рим не за один день строился» применима и к технологиям. Такие инструменты, как grunt , и такие платформы, как Node.js, созревали годами. В веб-мире все движется быстро, и каждый день появляется огромное количество фреймворков, пакетов и инструментов, поэтому разработчики склонны становиться немного более скептичными и не хотят прыгать на каждую подножку шумихи только для того, чтобы понять, что они оказались в ситуации блокировки поставщика. Мы рассмотрим, что делает React Native особенным, почему это технология, которую стоит изучить, и рассмотрим несколько случаев, когда это не все единороги и радуги.

Под капотом

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

Объединение веб-приложений в мобильный веб-браузер

Веб-приложение находится в мобильном браузере, обычно называемом WebView. Без какого-либо серьезного рефакторинга веб-сайт или веб-приложение работают на мобильном устройстве. Возможно, нам придется учитывать события мобильного браузера, такие как касание или прослушивание изменений ориентации устройства, а также меньший экран для полноценного взаимодействия с пользователем, но у нас есть работающая мобильная версия с минимальными усилиями. Cordova/PhoneGap — самый популярный вариант в этой категории. К сожалению, у этого варианта есть большой недостаток: в некоторых случаях приложения, разработанные с использованием Cordova, значительно медленнее, чем нативные приложения, особенно для графических приложений. В других случаях мобильная операционная система фактически не предоставляет все функции WebView, доступные в мобильном браузере. Пользовательский интерфейс также может отличаться от нативных приложений; это может произойти из-за приложения или самой платформы. Эта проблема может варьироваться от полос прокрутки, которые не ощущаются одинаково, до заметной задержки при нажатии на элементы.

Компиляция в собственные технологии

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

Использование слоя JavaScript

Здесь мы используем движок JavaScript мобильной среды и выполняем там наш JavaScript. Собственные элементы управления сопоставляются с объектами и функциями JavaScript, поэтому при вызове функции fancyButtonRightHere() на экране появлялась кнопка. NativeScript или Appcelerator Titanium — хорошо известные примеры этой категории.

React Native можно отнести к третьей категории. В версиях для iOS и Android React Native использует JavaScriptCore под капотом, который является движком JavaScript по умолчанию в iOS. JavaScriptCore также является движком JavaScript в браузерах Apple Safari. Разработчики OS X и iOS могут напрямую взаимодействовать с ним, если захотят.

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

React — ключевая особенность

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

Единая точка входа рендеринга

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

 var MyComponent = function() { this.render = function() { // Render something here }; };

Особенность заключается в том, что здесь мы не связываемся с элементами DOM, а возвращаем конструкцию на основе XML, которая представляет то, что будет отображаться в DOM. Эта конструкция на основе XML называется JSX.

 var MyComponent = function() { this.render = function() { return <div className="my-component">Hello there</div>; }; };

Специальный преобразователь JSX берет весь код, похожий на XML, и преобразует его в функции. Вот так будет выглядеть компонент после преобразования:

 var MyComponent = function() { this.render = function() { return React.createElement("div", { className: "my-component" }, "Hello there"); }; };

Самым большим преимуществом является то, что, взглянув на компонент, мы всегда знаем, что он должен делать. Например, компонент <FriendList /> может отображать ряд компонентов <Friend /> . Мы не можем визуализировать наши компоненты где-либо еще, кроме как внутри функции render , поэтому никогда не возникает беспокойства, что мы не знаем, откуда именно взялся наш визуализированный компонент.

Однонаправленный поток данных

Чтобы создать содержимое компонента, React предоставляет свойства или реквизиты для краткости. Подобно атрибутам XML, мы передаем реквизиты непосредственно компоненту, а затем можем использовать реквизиты внутри сконструированного компонента.

 var Hello = function(props) { this.render = function() { return <div className="my-component">Hello {props.name}</div>; }; }; var Greeter = function() { this.render = function() { return <Hello name="there" /> } };

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

Повторный рендеринг при изменениях

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

Каждое изменение свойства и состояния вызывает полную повторную визуализацию компонента.

Виртуальный дом

Теперь, когда все перерисовывается при изменении реквизита или состояния, почему сам React работает так хорошо? Волшебный ингредиент — «Виртуальный DOM». Всякий раз, когда что-то необходимо перерендерить, создается виртуальное представление обновленного DOM. Виртуальный DOM состоит из упрощенных представлений элементов, смоделированных по образцу дерева компонентов, что делает процесс их создания намного более эффективным, чем создание реальных элементов DOM. Перед применением изменений к реальному DOM выполняются проверки, чтобы определить, где именно в дереве компонентов произошли изменения, создается diff и применяются только эти конкретные изменения.

Начало работы с этим руководством по React Native

Существуют определенные предварительные условия, которые необходимо установить новичкам для разработки для React Native. Поскольку iOS была первой поддерживаемой платформой, которую мы рассматриваем в этом руководстве, нам нужны macOS и Xcode, по крайней мере, версии 6.3. Node.js также необходим. Что помогает, так это установить Watchman через менеджер пакетов Brew с помощью brew install watchman . Хотя это не обязательно необходимо, это помогает при работе с большим количеством файлов внутри нашего проекта React Native.

React Native: самая разумная среда разработки мобильных приложений.
Твитнуть

Чтобы установить React Native, нам просто нужно установить приложение командной строки React Native с помощью npm install -g react-native-cli . Затем вызов команды react-native помогает нам создать новое приложение React Native. При запуске react-native init HelloWorld создается папка HelloWorld , в которой можно найти шаблонный код.

Анимация терминала, показывающая, как настроить приложение React Native «Hello World».

Преобразование приложения React

Поскольку React является ключевой функцией и основными принципами библиотеки React, давайте посмотрим, что нам нужно, чтобы преобразовать минимальное приложение React «Hello World» в приложение React Native.

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

 var React = require('react'); class HelloThere extends React.Component { clickMe() { alert('Hi!'); } render() { return ( <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

Шаг 1: Используйте модули CommonJS

На первом этапе нам нужно изменить требование, чтобы модуль React вместо этого использовал react-native .

 var React = require('react-native'); class HelloThere extends React.Component { clickMe() { alert('Hi!'); } render() { return ( <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

То, что обычно является частью конвейера инструментов при разработке веб-приложения React, является неотъемлемой частью React Native.

Шаг 2: нет DOM

Неудивительно, что на мобильных устройствах нет DOM. Там, где мы ранее использовали <div /> , нам нужно использовать <View /> , а там, где мы использовали <span /> , нам нужен компонент <Text /> .

 import React from 'react'; import {View, Text, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert('hi!'); } render() { return ( <View className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</View> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

Хотя довольно удобно размещать текст непосредственно в элементах <div /> , в родном мире текст нельзя поместить непосредственно в элемент <View /> . Для этого нам нужно вставить компонент <Text /> .

 import React from 'react'; import {View, Text, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert('hi!'); } render() { return ( <View className="box" onClick={this.clickMe.bind(this)}> <Text>Hello {this.props.name}. Please click me.</Text> </View> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

Шаг 3. Встроенные стили — правильный путь

React Native позволяет нам использовать моделирование Flexbox вместо того, чтобы возиться с float и inline-block , с которыми мы так хорошо знакомы в веб-мире. Интересно то, что React Native не использует CSS.

 import React from 'react'; import {View, Text, StyleSheet, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert('hi!'); } render() { return ( <View style={styles.box} onClick={this.clickMe.bind(this)}> <Text>Hello {this.props.name}. Please click me.</Text> </View> ); } } var styles = StyleSheet.create({ box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 } }); React.render(<HelloThere name="Component" />, document.getElementById('content'));

Использование встроенных стилей кажется начинающим сбивающим с толку. Это похоже на переход, который пришлось пройти разработчикам React, когда они столкнулись с JSX и ранее использовали механизмы шаблонов, такие как Handlebars или Jade.

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

 import React from 'react'; import {Text} from 'react-native'; var Headline = function(props) { this.render = () => <Text style={headlineStyle.text}>{props.caption}</Text>; }; var headlineStyles = StyleSheet.create({ text: { fontSize: 32, fontWeight: 'bold' } }); module.exports = Headline;

Шаг 4: Обработка событий

Эквивалентом щелчка на веб-странице является нажатие элемента на мобильном устройстве. Давайте изменим наш код, чтобы «оповещение» появлялось при касании элемента.

 import React from 'react'; import {View, Text, StyleSheet, TouchableOpacity, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert("Hi!") } render() { return ( <TouchableOpacity onPress={this.clickMe()}> <View style={styles.box}> <Text>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> ); } } var styles = StyleSheet.create({ box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 } }); React.render(<HelloThere name="Component" />, document.getElementById('content'));

Вместо того, чтобы события были непосредственно доступны в компонентах <View /> , нам нужно явно использовать элементы, которые запускают события, в нашем случае событие касания при нажатии на представление. Доступны различные типы сенсорных компонентов, каждый из которых обеспечивает различную визуальную обратную связь.

Шаг 5. Настройте поведение на разных платформах

Можно определить, на какой платформе работает приложение React Native, обратившись к значению Platform.OS . Допустим, в приведенном выше примере мы хотели отобразить другое предупреждающее сообщение в зависимости от платформы, на которой мы работаем. Мы можем сделать это так:

 ... clickMe() { var message = ''; if(Platform.OS == 'ios') { message = 'Welcome to iOS!'; } else if(Platform.OS == 'android') { message = 'Welcome to Android!'; } Alert.alert(message); } ...

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

 … clickMe() { Alert.alert(Platform.select({ ios: 'Welcome to iOS!', android: 'Welcome to Android!' }) ); } ...

Шаг 6: Пользовательские шрифты и react-native link

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

Итак, если полное имя вашего шрифта — myCustomFont , убедитесь, что имя файла шрифта — myCustomFont.ttf .

После этого нам нужно создать папку с ресурсами и указать на нее npm. Мы можем сделать это, сначала создав папку в assets/fonts в корневом каталоге приложения. Подойдет любой другой каталог, но это обычное имя, используемое для каталога шрифтов.

Мы можем указать npm, где у нас есть наши активы, добавив свойство Assets в разделе интеграции React с npm, rnpm:

 "rnpm": { "Assets": [ "./assets/fonts/" ] }

После того, как мы все это сделали, мы, наконец, можем запустить react-native link . Это скопирует шрифты в нужные каталоги и добавит необходимый xml в info.plist на iOS.

После этого мы можем использовать наш шрифт, просто сославшись на него в любой таблице стилей по его полному имени. Давайте используем его в нашем Text элементе:

 import React from 'react'; import {View, Text, StyleSheet, TouchableOpacity, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert("Hi!") } render() { return ( <TouchableOpacity onPress={this.clickMe()}> <View style={styles.box}> <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> ); } } var styles = StyleSheet.create({ box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 }, message: { fontFamily: 'myCustomFont' } }); React.render(<HelloThere name="Component" />, document.getElementById('content'));

Шаг 7: Перемещение вещей

React Native использует те же правила, что и Flexbox, для компоновки компонентов. Скажем, мы хотели расположить нашу кнопку внизу экрана: давайте обернем наш TouchableOpacity контейнером View :

 <View style={styles.container}> <TouchableOpacity onPress={this.clickMe.bind(this)}> <View style={styles.box}> <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> </View>

А теперь давайте определим стиль container вместе с другими уже определенными стилями:

 container: { flex: 1, justifyContent: 'center', alignItems: 'center' }

Давайте сосредоточимся на justifyContent и alignItems . Эти два свойства управляют тем, как компонент выравнивается соответственно по его основной и вторичной осям. По умолчанию основной осью является вертикальная, а дополнительной осью — горизонтальная ось (это можно изменить, установив для свойства flexDirection значение row ).

justifyContent имеет шесть возможных значений:

  • flex-start поместит все элементы вместе в начало ограничивающей рамки компонента.
  • flex-end поместит все элементы в конец.
  • center поместит все элементы в центр ограничивающей рамки.
  • space-around равномерно распределяет компоненты и центрирует компоненты в созданных ими ограничивающих прямоугольниках.
  • space-evenly распределит компоненты, но попытается оставить равное пространство между компонентами и другими границами.
  • space-between распределяет компоненты, сохраняя расстояние между соседними компонентами равными.

alignItems может принимать четыре возможных значения: flex-start , flex-end , center и stretch . Первые три ведут себя так же, как и для justifyContent , в то время как stretch заставит компонент занимать все доступное пространство вдоль оси, так что ось будет полностью заполнена.

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

 container: { flex: 1, justifyContent: 'flex-end', alignItems: 'center' }

Дополнительную информацию о значениях, которые могут иметь justifyContent и alignItems , можно найти здесь и здесь.

Шаг 8: Регистрация приложения

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

 import React from 'react'; import {View, Text, StyleSheet, TouchableOpacity, Alert, Platform} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert(Platform.select({ ios: 'Welcome to iOS!', android: 'Welcome to Android!' })); } render() { return ( <View style={styles.container}> <TouchableOpacity onPress={this.clickMe.bind(this)}> <View style={styles.box}> <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> </View> ); } } var styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'flex-start', alignItems: 'center' }, box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 }, message: { fontFamily: 'myCustomFont' } }); var MainComponent = function() { this.render = function() { return <HelloThere name="Component" />; } }; AppRegistry.registerComponent('MainComponent', function() { return MainComponent; });

Мы должны зарегистрировать компонент для цели-C, что делается с помощью объекта AppRegistry . Имя, которое мы даем, должно совпадать с именем внутри проекта Xcode.

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

В качестве примечания: мы не должны перепривязывать метод clickMe к контексту this в методе render , особенно если наше приложение React (Native) становится немного сложнее. Он перепривязывает метод при каждом вызове рендеринга, которых может стать довольно много. Альтернативой является привязка метода внутри конструктора.

Запуск приложения

Чтобы запустить приложение, нам нужно заменить содержимое файла index.ios.js на фрагмент кода нашего преобразованного приложения из последнего шага. Затем нам просто нужно открыть проект Xcode и нажать большую кнопку «Выполнить». Сначала откроется терминал с сервером React Native, а затем появится окно симулятора. Сервер React Native создает пакет, который затем получает собственное приложение. Это позволяет использовать цикл быстрой разработки, подобный веб-разработке, когда изменения практически мгновенно отражаются в симуляторе.

Для Android достаточно добавить в файл package.json в scripts следующее:

 "android-linux": "react-native bundle --platform android --dev false --entry-file index.ios.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/ main/res && react-native run-android"

Затем запустите npm run android-linux . Убедитесь, что каталог android/app/src/main/assets существует заранее.

После появления терминала наше приложение появится в симуляторе. Нажатие CMD+D покажет меню разработки. При нажатии на поле появится предупреждение. Версия iOS:

Телефон Apple с всплывающим окном с надписью «Привет».

И Android отображает что-то вроде этого:

Телефон Android с всплывающим окном с текстом «Привет».

Для дистрибутива наличие приложения, указывающего на локальный сервер разработки, нам не подходит. По этой причине мы можем создать пакет для использования, когда сервер React Native не работает, с помощью команды react-native bundle . В этом случае нам нужно обновить метод didFinishLaunchingWithOptions AppDelegate , чтобы использовать автономный пакет.

Этот пример приложения также доступен на Github.

Работа с React Native

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

Принцип работы React Native заключается в том, что он помещает все наши файлы JavaScript в пакет. Этот пакет либо обслуживается сервером, либо поставляется вместе с приложением. Первый невероятно полезен для разработки в Симуляторе, так как мы можем включить перезагрузку в реальном времени. Меню разработчика, предоставляемое React, ни в коем случае не такое мощное, как инструменты разработчика Chrome, но оно обеспечивает очень похожий на Интернет опыт разработчика с перезагрузкой и отладкой в ​​​​режиме с помощью инструментов разработчика / отладчика Chrome (или Safari).

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

React Native: надежный современный выбор

Изначально я предлагал более осторожный подход к React Native. Сегодня это зрелый и солидный выбор.

Одним из больших преимуществ React является то, что он не влияет на ваш рабочий процесс, поскольку он просто представляет слой представления. Вы хотите определить свой собственный конвейер Grunt? Или вы предпочитаете использовать Webpack? И будете ли вы использовать Backbone.js для нужд вашей модели? Или вы хотите использовать простые объекты JavaScript? Ответы на все эти вопросы полностью зависят от вас, потому что React не накладывает никаких ограничений на этот выбор. Как было сказано на официальном сайте: «Поскольку React не делает никаких предположений об остальном стеке ваших технологий, его легко опробовать на небольшой функции в существующем проекте».

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

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

Связанный: Создание QR-сканера: Учебник React Native Camera