Это может показаться невероятным, но можно превратить ваше веб-приложение React в собственное мобильное приложение без использования React Native.

Это стало возможным благодаря последним достижениям в области веб-технологий. Подобно тому, как Electron позволяет создавать настольные приложения с использованием веб-кода, Capacitor позволяет делать то же самое для мобильных приложений.

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

Настройка приложения

Для начала мы настроим базовое приложение React, используя инструмент «create-react-app» и шаблон TypeScript.

Во-первых, на вашем компьютере должны быть установлены Node.js и npm (менеджер пакетов Node). Получив это, вы можете использовать следующую команду для создания нового приложения React с поддержкой TypeScript:

npx create-react-app capacitor-app --template typescript

cd ./capacitor-app

npm i react-router-dom

Для перехода на разные страницы требуется только дополнительная зависимость от маршрутизатора.

Создайте пустую папку с именем «routes» в каталоге «src». Затем в папке «routes» создайте два новых файла с именами «Dashboard.tsx» и «Login.tsx». Эти файлы пока можно заполнить кодом-заполнителем.

function Dashboard() {

  return (
    <>
      Dashboard
    </>
  );
}

export default Dashboard;

Следуйте той же схеме для страницы входа и переименуйте ключевые слова в Login!

С нашими двумя страницами мы можем определить маршруты для маршрутизатора React внутри нашего src/index.tsx:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import {
  BrowserRouter,
  Routes,
  Route,
  Link,
} from "react-router-dom";
import Login from './routes/Login';
import Dashboard from './routes/Dashboard';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />}>
          <Route path="" element={<Login />} />
          <Route path="/app/dashboard" element={<Dashboard/>} />
          <Route
            path="*"
            element={
              <main style={{ padding: "1rem" }}>
                <p>There's nothing here!</p>
                <Link to="/">Back home!</Link>
              </main>
            }
          />
        </Route>
      </Routes>
    </BrowserRouter>
  </React.StrictMode>
);

В приведенном выше коде мы настроили BrowserRouter, указав маршрут «/» для использования компонента App. Мы также добавили все остальные маршруты ниже этого, чтобы маршрутизатор всегда сначала загружал файл приложения, а затем разрешал остальную часть пути. Если другой компонент пути отсутствует, будет отображаться компонент Login, а для «/app/dashboard» будет использоваться компонент Dashboard. Если нет подходящего пути, у нас есть резервный HTML-код, но вместо этого мы могли бы легко использовать собственную страницу 404.

Мы используем путь с подстановочными знаками, а также напрямую используем элемент Link из маршрутизатора React для запуска правильного маршрута в нашем приложении. Кроме того, нам нужно добавить Outlet в наше представление, чтобы React Router мог отображать фактические элементы внутри него.

Мы решили сделать это в элементе App, который является родителем всех маршрутов, поэтому нам нужно соответствующим образом отредактировать src/App.tsx:

import './App.css';
import { Outlet } from "react-router-dom";

function App() {
  return (
    <div style={{ margin: "1rem" }}>
    <h1>Dave's App</h1>
    <Outlet />
  </div>
  );
}

export default App;

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

На этом этапе вы должны быть в состоянии обслуживать свое приложение, запустив:

npm run start

Вы можете видеть, что страница по умолчанию является логином, и вы также можете напрямую перейти к http://localhost:3000/app/dashboard, чтобы увидеть вторую страницу.

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

Создание простого входа в React

Чтобы упростить процесс, мы смоделируем процесс загрузки для входа в систему и воспользуемся хуком useNavigate для перехода к панели управления из нашего кода.

Кроме того, мы можем использовать хук useState для отображения или скрытия индикатора загрузки во время «отправки» формы. Обновите файл src/routes/Login.tsx, чтобы отразить эти изменения:

import { useState } from "react";
import { useNavigate } from "react-router-dom";

function Login() {
  const [loading, setLoading] = useState(false);
  const navigate = useNavigate();

  const onSubmit = (event: any) => {
    event.preventDefault();
    setLoading(true);

    console.log("submit!");

    setTimeout(() => {
      setLoading(false);
      navigate("/app/dashboard");
    }, 1500)
  };

  return (
    <main style={{textAlign: "center"}}>
      <h2>Login</h2>
      <form onSubmit={onSubmit}>
        <div style={{display: "flex", flexDirection: "column", alignItems: "center", gap: "10px"}}>
          <div>
            <label>Email:</label>
            <input type="text" ></input>
          </div>

          <div>
            <label>Password:</label>
            <input type="password"></input>
          </div>

          {loading ? <div>Loading...</div> :
          <button type="submit">Login</button>
        }
        </div>
      </form>
    </main>
  );
}

export default Login;

Не уверены в этих крючках? Ознакомьтесь с документацией useState, чтобы узнать больше об этом!

На нашей странице панели инструментов мы просто отобразим другой заголовок и кнопку для возврата, поэтому откройте файл src/routes/Dashboard.tsx и быстро измените его на:

import { useNavigate } from "react-router-dom";

function Dashboard() {
  const navigate = useNavigate();

  const onLogout = () => {
    navigate('/');
  };

  return (
    <>
      <h2>Dashboard</h2>
      <button onClick={onLogout}>Logout</button>
    </>
  );
}

export default Dashboard;

Здесь нет ничего особенного.

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

Установка конденсатора

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

Сначала установите Capacitor CLI локально, а затем инициализируйте его в своем проекте. Затем установите основной пакет, а также пакеты для платформ iOS и Android. Наконец, вы можете добавить платформы, и все готово!

# Install the Capacitor CLI locally
npm install @capacitor/cli --save-dev

# Initialize Capacitor in your React project
npx cap init

# Install the required packages
npm install @capacitor/core @capacitor/ios @capacitor/android

# Add the native platforms
npx cap add ios
npx cap add android

После этих шагов вы должны увидеть новую папку «ios» и «android» в вашем проекте React. Это настоящие нативные проекты! Чтобы открыть проект Android, вам нужно установить Android Studio, а для iOS вам нужно быть на Mac и установить Xcode.

Кроме того, вы должны увидеть в своем проекте файл «capacitor.config.ts», в котором содержатся некоторые основные настройки конденсатора, используемые в процессе синхронизации. Единственное, о чем вам нужно беспокоиться, это «webDir», который должен указывать на вывод вашей команды сборки, но это уже должно быть правильно.

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

npm run build
npx cap sync

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

Даже не осознавая этого, вы закончили. Давайте посмотрим на приложение на устройстве!

Создавайте и развертывайте нативные приложения

Чтобы создавать и распространять приложения в App Store и Google Play Store, на вашем компьютере должны быть установлены приложения Xcode для iOS и Android Studio для Android. Кроме того, вы должны быть зарегистрированы в программе разработчиков Apple для распространения приложений iOS и в консоли разработчика Google Play для распространения приложений Android.

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

npx cap open ios
npx cap open android

После того, как вы открыли проект в Android Studio, вам нужно будет дождаться загрузки необходимых файлов. После этого вы можете развернуть приложение на подключенном устройстве, не внося никаких изменений в настройки!

Как и в Android Studio, после того, как вы открыли проект в Xcode, вам нужно будет настроить свою учетную запись для подписи, если вы хотите развернуть свое приложение на физическом устройстве, а не только в симуляторе. Xcode проведет вас через этот процесс, если вы никогда раньше этого не делали, но, как упоминалось ранее, вам нужно будет зарегистрироваться в программе Apple Developer Program.

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

Поздравляем, вы успешно развернули свое веб-приложение React на мобильном устройстве!

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

Конденсатор Live Reload

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

Идея состоит в том, чтобы сделать ваше локально обслуживаемое приложение с перезагрузкой в ​​реальном времени доступным в вашей сети, а приложение Capacitor просто загрузит контент с этого URL-адреса.

Первым шагом является определение вашего локального IP-адреса, который вы можете получить на Mac, выполнив следующую команду:

ipconfig getifaddr en0

В Windows вы можете запустить команду «ipconfig» и найти адрес IPv4.

Далее нам нужно настроить Capacitor для загрузки приложения непосредственно с этого сервера. Это можно сделать в файле «capacitor.config.ts», добавив следующую запись:

import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'capacitor-app',
  webDir: 'build',
  bundledWebRuntime: false,
  server: {
    url: 'http://192.168.x.xx:3000',
    cleartext: true
  },
};

export default config;

Убедитесь, что вы используете правильный IP и порт. В этом примере я использовал порт React по умолчанию.

Чтобы применить эти изменения, теперь мы можем скопировать их в наш собственный проект:

npx cap copy

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

Теперь вы можете снова развернуть свое приложение через Android Studio или Xcode, а затем внести изменения в свое приложение React.

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

Исправить мобильный интерфейс React

Теперь мы можем более легко решать нерешенные проблемы с помощью нашего мобильного приложения.

Для начала нам нужно изменить метатег в нашем файле «public/index.html» и включить «viewport-fit», чтобы исправить проблемы с пользовательским интерфейсом на iOS.

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />

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

Для этого откройте файл «src/App.tsx» и включите значение в качестве paddingTop для нашего элемента-оболочки.

import './App.css';
import { Outlet } from "react-router-dom";

function App() {
  return (
    <div style={{ margin: "0 1rem 0 1rem", paddingTop: "env(safe-area-inset-top)" }}>
    <h1>Dave's App</h1>
    <Outlet />
  </div>
  );
}

export default App;

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

Однако, если вы также хотите протестировать простой способ добавления адаптивного мобильного стиля в наше приложение React, мы можем сделать еще один шаг вперед.

Использование компонентов Ionic UI с React

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

Чтобы использовать его, нам нужно только установить пакет Ionic React сейчас:

npm install @ionic/react

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

npm install ionicons

Чтобы использовать стиль Ionic, теперь нам нужно изменить наш src/App.tsx и включить:

/* Core CSS required for Ionic components to work properly */
import '@ionic/react/css/core.css';

/* Basic CSS for apps built with Ionic */
import '@ionic/react/css/normalize.css';
import '@ionic/react/css/structure.css'; // Remove if nothing is visible
import '@ionic/react/css/typography.css';

/* Optional CSS utils that can be commented out */
import '@ionic/react/css/padding.css';
import '@ionic/react/css/float-elements.css';
import '@ionic/react/css/text-alignment.css';
import '@ionic/react/css/text-transformation.css';
import '@ionic/react/css/flex-utils.css';
import '@ionic/react/css/display.css';
import { setupIonicReact } from '@ionic/react';

setupIonicReact();

Чтобы использовать компоненты Ionic, нам нужно импортировать их один за другим из только что установленного пакета и использовать их вместо элементов div, которые у нас были раньше.

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

Кроме того, есть хуки, такие как useIonAlert или useIonLoading, которые позволяют нам вызывать определенные оверлеи или компоненты Ionic непосредственно из кода.

Это означает, что теперь мы можем легко отобразить экран загрузки, вызвав функцию present() и отобразив собственное модальное оповещение с помощью функции alert() с соответствующими значениями для элементов внутри оповещения.

Теперь вы можете изменить файл src/routes/Login.tsx, чтобы отразить эти изменения.

import { IonButton, IonCard, IonCardContent, IonIcon, IonInput, IonItem, IonLabel, useIonAlert, useIonLoading } from "@ionic/react";
import { useNavigate } from "react-router-dom";
import { logIn } from 'ionicons/icons';

function Login() {
  const navigate = useNavigate();
  const [alert] = useIonAlert();
  const [present, dismiss] = useIonLoading();

  const onSubmit = async (event: any) => {
    event.preventDefault();
    await present({message: 'Loading...'})

    setTimeout(() => {
      dismiss();
      if(Math.random() < 0.5) {
        alert({
          header: 'Invalid credentials',
          message: 'There is no user with that name and password.',
          buttons: [{text: 'Ok'}]
        })
      } else {
        navigate("/app/dashboard");
      }
    }, 1500)
  };

  return (
    <>
      <IonCard>
        <IonCardContent>
          <form onSubmit={onSubmit}>
            <IonItem>
              <IonLabel position="floating">Email</IonLabel>
              <IonInput type="email"></IonInput>
            </IonItem>

            <IonItem>
              <IonLabel position="floating">Password</IonLabel>
              <IonInput type="password"></IonInput>
            </IonItem>

            <div className="ion-margin-top">
            <IonButton expand="full" type="submit" color="secondary">
                <IonIcon icon={ logIn } slot="start"/>
                Login</IonButton>
            </div>
          </form>
        </IonCardContent>
      </IonCard>
    </>
  );
}

export default Login;

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

Заключение

Преобразование приложения React в собственное мобильное приложение для iOS и Android может быть относительно простым, без необходимости использовать React Native и изучать множество новых библиотек.

Если у вас есть опасения по поводу производительности, перед принятием решения стоит взглянуть на сравнение производительности между Ionic и React Native.

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