Інтеграція React SPA або статичних HTML-сторінок у React Native за допомогою React-native-static-server і WebView

Усі статті, обговорення, новини про Mobile — в одному місці. Підписуйтеся на телеграм-канал!

Всім привіт. Мене звати Нікіта Усіченко, я Front-end developer в компанії MEV.

У цій статті я розпишу інструкцію з інтеграції React Single Page Application (SPA) або статичної HTML-сторінки в застосунок React Native за допомогою react-native-static-server та WebView для платформ iOS та Android.

Цей гайд буде корисний для розробників, які бажають розширити можливості мобільних застосунків за допомогою вебфункцій. Для отримання практичних прикладів перегляньте репозиторії на GitHub: card-caster-web і card-caster-go.

Вступ

Уявіть світ, у якому всі мобільні застосунки не тільки бездоганно працюють, але й можуть похвалитись привабливим візуальним дизайном. Звучить трохи фантастично? Коли ми маємо справу з React Native, це завдання справді може бути дещо складним, особливо, якщо нам потрібно додати хитромудрі анімації або крутий дизайн.

Часто вебсайти справляються із цим набагато краще. Особливо відчутна різниця, якщо ви спробуєте додати у застосунок сторінку з великою кількістю рухомих зображень чи складних даних. Те, що виглядає чудово на сайті, у React Native може працювати повільно або не працювати взагалі.

А тепер уявіть, що ми можемо взяти найкращі частини вебсайту (зокрема ті самі ефектні анімації) та додати їх у свій react-native застосунок. Ба більше, якщо ви вже створили вебсайт, вам не доведеться створювати все заново для застосунку.

Власне, саме про це ми й будемо говорити далі: грубо кажучи, як розмістити мінівеб-сайт у застосунку React Native.

Основи

Перш ніж ми заглибимося в інструкції, познайомимось з основними елементами нашого проєкту.

React Single Page Application (SPA), або HTML-сторінка

Я надам інструкцію із запуску React Single Page Application (SPA) у React Native-застосунку. А якщо ви працюєте з простими HTML-сторінками, ті ж кроки підійдуть і для них.

До кінця ви будете точно знати, як інтегрувати будь-який вебконтент у React Native-застосунок, покращуючи його функціональність і зручність для користувача.

React Native

React Native — це щось на кшталт набору інструментів, який дозволяє вам створити застосунки як на Android, так і на iOS.

Навіщо їх змішувати

Іноді у вашому застосунку потрібно показати щось справді круте чи складне, наприклад гру чи графік, який рухається та змінюється. Зробити це в React Native може бути складно, і застосунок може працювати повільно.

Але у деяких випадках зробити це у вигляді SPA може бути простіше і краще.

Як ми це робимо

Щоб цей мікс працював, ми використовуємо два помічники: response-native-static-server та WebView. Статичний сервер нагадує мінімережу, яка працює всередині вашого телефону і містить ваш SPA.

WebView — це вікно вашої програми, в якому відображається ваш SPA, як у мінібраузері.

Об’єднавши ці два компоненти, ми створимо у вашому застосунку місце, де вебконтент зможе жити та відтворюватися так само легко, як на комп’ютері.

Налаштування

Створіть ваш вебзастосунок

Якщо ви починаєте з нуля з React SPA, налаштуйте свій проєкт за допомогою create-react-app для швидкого старту:

npx create-react-app my-web-app

Потім розробіть свій односторінковий вебзастосунок (SPA) за бажання, переконавшись, що він є адаптивним для мобільних пристроїв, або скористайтеся наявним прикладом SPA.

Створіть свій застосунок на React Native

На момент написання цієї статті актуальною є версія React Native 0.72.6. Для початку роботи з цією версією ініціалізуйте свій проєкт, виконавши наступну команду:

npx react-native init CardCasterGo --version 0.72.6

Якщо ви віддаєте перевагу автоматичному використанню останньої стабільної версії, просто виконайте наступну команду:

npx react-native init CardCasterGo

Або використай наявний react-native app example.

Встановлення статичного сервера та додаткових залежностей

Ми використовуватимемо @dr.pogodin/react-native-static-server для надання вебконтенту в межах застосунку. Це підтримувана версія оригінального futurepress/react-native-static-server, яка забезпечує сумісність з нашими поточними інтерфейсами.

Ми будемо встановлювати версію 0.6.0-alpha.8, але слід зазначити, що є новіші версії. Тож якщо вам цікаво, у цій статті ви знайдете посилання на ці новіші версії та зможете використовувати їх, якщо вони відповідають вимогам вашого проєкту.

Встановіть статичний сервер та додаткові необхідні бібліотеки, виконавши команду:

yarn add @dr.pogodin/[email protected] react-native-fs react-native-webview

Ця команда встановлює react-native-static-server, react-native-fs для доступу до файлової системи, і react-native-webview для рендерингу вебзмісту в межах застосунку.

Не забудьте перейти в папку iOS та виконати команду:

pod install

Підготовка вебконтенту

Тепер, коли налаштовано ваше середовище розробки, час готувати вебконтент для інтеграції.

Незалежно від того, чи ви працюєте з React SPA або простішою HTML-сторінкою, процес передбачає, що контент готовий для подачі у вашому застосунку React Native.

Після завантаження прикладу SPA або створення власного перейдіть до каталогу проєкту в терміналі та виконайте команду:

npm run build

Ця команда компілює ваш застосунок у статичні файли для виробництва в каталозі збірки.

Перевірте ваш білд

Відкрийте папку зі збіркою та переконайтеся, що вона містить файл index.html разом з необхідними JavaScript та CSS ресурсами. Ви можете запустити збірку за допомогою наступної команди:

npx serve -s build

Загальні поради

  • Переконайтеся, що ваш вебконтент є адаптивним. Він повинен виглядати добре на різних розмірах екрана, зокрема на мобільних екранах.
  • Використовуйте відносні шляхи для ресурсів у ваших HTML- або SPA-файлах. Це гарантує, що файли можна знайти за обслуговування з різних базових каталогів у середовищі React Native.
  • Оптимізуйте зображення та мінімізуйте CSS та JavaScript для покращення часу завантаження.
  • Тестуйте свій вебконтент у різних браузерах, щоб гарантувати сумісність, оскільки WebView може поводитися по-різному на iOS та Android.

Налаштування статичного сервера для iOS

Для iOS після збірки вебзастосунка перше, що вам потрібно зробити, — це перемістити теку build в корінь вашого застосунку React Native. Це гарантує правильне розташування контенту в структурі вашого проєкту для зручного доступу та інтеграції.

Ось як це зробити:

  1. Знайдіть теку build у вашому проєкті вебзастосунка. Ця тека містить зібрані статичні файли вашого вебконтенту.
  2. Перетягніть цю теку build в кореневий каталог вашого проєкту React Native. Там розташовані файли App.js та package.json вашого застосунку.

Коли ви переконаєтесь, що тека build перебуває у правильному місці, виконуйте ці кроки в Xcode, щоб упакувати вміст із вашим iOS-застосунком:

Перейдіть до теки ios у каталозі вашого проєкту React Native та відкрийте її, щоб запустити Xcode.

У Xcode клацніть на навігаторі проєкту та розгорніть першу теку, названу за іменем вашого проєкту.

Клацніть правою кнопкою миші на імені проєкту або відповідній підгрупі в навігаторі проєкту. Виберіть Add Files to «YourAppName» з контекстного меню.

У діалоговому вікні вибору файлу перейдіть до теки build і виберіть, де зберігається ваш вебконтент.

У діалозі, який з’являється під час додавання теки, переконайтеся, що опція Copy items if needed не відзначена. Це запобігає дублюванню теки в межах проєкту.

Зрештою ваше дерево тек має виглядати приблизно так:

Налаштування статичного сервера в React Native на iOS

У верхній частині вашого файлу імпортуйте StaticServer від @dr.pogodin/react-native-static-server та RNFS від react-native-fs.

import StaticServer from '@dr.pogodin/react-native-static-server';
import RNFS from 'react-native-fs';

Створіть власний хук useStaticServer. Цей хук буде відповідати за життєвий цикл статичного сервера.

const useStaticServer =()=>{
const [url, setUrl] = useState<string>('');
useEffect(() => {
  let server: StaticServer | null = null;
  const startServer = async (): Promise<void> => {
    const path: string = `${RNFS.MainBundlePath}/build`;
    server = new StaticServer(9090, path, {localOnly: true});
    try {
      const serverUrl = await server.start();
      setUrl(serverUrl);
    } catch (error) {
      console.error('Failed to start server:', error);
    }
  };
  startServer();
  return () => server?.stop();
}, []);
return url;
}

У своєму компоненті використовуйте хук useStaticServer для запуску сервера та отримання URL відданого контенту.

const serverUrl = useStaticServer();

Використовуйте компонент WebView з бібліотеки react-native-webview, щоб відображати ваш вебконтент.

import { WebView } from 'react-native-webview';
function MyComponent() {
 const serverUrl = useStaticServer();
 return (
   <WebView source={{ uri: serverUrl }} />
 );
}

Запустіть свою програму на симуляторі або пристрої iOS, щоб переконатися, що вебконтент правильно обслуговується та відображається.

Налаштування статичного сервера для Android

Під час включення вебконтенту до вашого застосунку React Native для Android вам потрібно виконати деякі додаткові кроки порівняно з iOS через конкретні вимоги платформи Android.

Ось покроковий гайд для цього.

Підготовка вебконтенту

Компіляція вебзастосунку. Розпочніть з компіляції вашого вебзастосунку в статичні файли. Зазвичай це призводить до створення папки build, в якій розташований ваш вебконтент.

Ручне переміщення папки build: вручну перемістіть папку build у каталог android/app/src/main/assets у вашому проєкті React Native. Якщо каталог assets відсутній за цим шляхом, створіть його.

Налаштування статичного сервера для Android

Файлова система Android не дозволяє обслуговувати файли безпосередньо з папки assets. Тому потрібен обхідний механізм, щоб зробити ці файли доступними для статичного сервера.

Реалізуйте функцію copyAssetsFolderContents у своєму коді React Native. Ця функція буде копіювати вміст вашої папки build з assets в каталог, який може бути доступний для статичного сервера. Приклад поточної функції:

const ASSETS_FOLDER_NAME: string = 'build';
const DOCUMENT_FOLDER_PATH: string = `${RNFS.DocumentDirectoryPath}/${ASSETS_FOLDER_NAME}`;
const copyAssetsFolderContents = async (
sourcePath: string,
targetPath: string,
): Promise<void> => {
try {
  const items = await RNFS.readDirAssets(sourcePath);
  const targetExists = await RNFS.exists(targetPath);
  if (!targetExists) {
    await RNFS.mkdir(targetPath);
  }
  for (const item of items) {
    const sourceItemPath = `${sourcePath}/${item.name}`;
    const targetItemPath = `${targetPath}/${item.name}`;
    if (item.isDirectory()) {
      await copyAssetsFolderContents(sourceItemPath, targetItemPath);
    } else {
      await RNFS.copyFileAssets(sourceItemPath, targetItemPath);
    }
  }
} catch (error) {
  console.error('Failed to copy assets folder contents:', error);
  throw error;
}
};
copyAssetsFolderContents(ASSETS_FOLDER_NAME, DOCUMENT_FOLDER_PATH)

Використовуйте хук useStaticServer для управління статичним сервером. Цей хук повинен запустити сервер після підтвердження успішного копіювання папки build в доступний каталог.

const useStaticServer = (folderWasCreated: boolean) => {
const [url, setUrl] = useState<string>('');
useEffect(() => {
  let server: StaticServer | null = null;
  const startServer = async (): Promise<void> => {
    const path: string = `${RNFS.DocumentDirectoryPath}/build`;
    server = new StaticServer(9090, path, {localOnly: true});
    try {
      const url = await server.start();
      setUrl(url);
    } catch (error) {
      console.error('Failed to start server:', error);
    }
  };
  if (folderWasCreated) {
    startServer();
  }
  return () => server?.stop();
}, [folderWasCreated]);
return url;
}
const [folderWasCreated, setFolderWasCreated] = useState<boolean>(false);
const url = useStaticServer(folderWasCreated);
useEffect(() => {
copyAssetsFolderContents(ASSETS_FOLDER_NAME, DOCUMENT_FOLDER_PATH)
  .then(() => {
    console.log('Build folder contents copied successfully.');
    setFolderWasCreated(true);
  })
  .catch(error => {
    console.error('Error copying build folder contents:', error);
  });
}, []);

Використовуйте компонент WebView з пакету react-native-webview для відображення контенту, який надається вашим статичним сервером.

<WebView source={{ uri: serverUrl }} />

Опрацювання помилки ERR_CLEARTEXT_NOT_PERMITTED

Часто за використання програми в режимі випуску або під час створення APK для Android виникає помилка ERR_CLEARTEXT_NOT_PERMITTED.

Її поява пов’язана з політикою безпеки Android щодо передачі чіткого тексту (трафік без HTTPS).

Ця проблема широко обговорюється на Stack Overflow. Ось що потрібно для її вирішення:

Створіть файл конфігурації мережі (Network Security Config)

1.1. У вашому проєкті Android перейдіть до директорії res/xml. Якщо цієї директорії не існує, створіть її.

1.2. Створіть новий файл із назвою network_security_config.xml у цій директорії.

1.3. Додайте наступну конфігурацію до цього файлу:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
   <domain-config cleartextTrafficPermitted="true">
       <!-- Insert specific domains here if needed -->
   </domain-config>
   <base-config cleartextTrafficPermitted="false"/>
</network-security-config>

Ця конфігурація дозволяє передачу відкритого тексту (не за HTTPS) для вказаних доменів, забезпечуючи безпечну політику для всіх інших.

Оновіть Android Manifest:

2.1 Відредагуйте файл AndroidManifest.xml, додавши наступний атрибут всередині тегу <application>:

android:networkSecurityConfig="@xml/network_security_config"

Після впровадження цих змін перекомпілюйте та протестуйте свій застосунок у режимі випуску. Забезпечте, що компонент WebView завантажує локальний вміст без спричинення помилки ERR_CLEARTEXT_NOT_PERMITTED.

Висновок

Ми дізналися, як об’єднати найкраще з веброзробки та створення мобільних застосунків, використовуючи React Native. Цей підхід дозволяє легко додавати стильні вебфункції, як-от деталізовані дизайни та анімації, до наших мобільних застосунків.

Зазирніть в репозиторії GitHub, щоб побачити повний приклад та як усе працює разом. Для повного прикладу застосунку:

Завдяки таким інструментам, як react-native-static-server та WebView, а також підтримці спільноти, ми можемо створювати потужні та приємні в користуванні мобільні застосунки. Спробуйте це та побачте, які неймовірні речі ви можете створити!

👍ПодобаєтьсяСподобалось3
До обраногоВ обраному3
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

Звісно, рішення автора має право на існування. Особливо, якщо це дійсно допоможе закрити таску, до якої тестувальник відкрив вже десяток багів і вирішити які на даному стеку технологій не виходить. Але сама ситуація досить цікава.
Згадаємо історію. Колись дуже-дуже давно, коли андроїд ледь дотягнув до версії 2.0, зʼявився такий фреймворк як phonegap (cordova). Він приніс революційну ідею: робити застосунки для телефонів у вигляді веб-сторінок та показувати в компоненті-браузері, загорнувши це все в рідний застосунок. Ідея швидко взлетіла, бо це було дійсно круто. Адже ми отримували крос-платформеність практично безкоштовно. Розробники (а особливо фронтендшики) з радістю прийняли таку технологію і почали швидко розробляти аплікухи для телефонів. Це було прекрасне відчуття. Не треба ні якої Джава, не Objective c. Нічого не треба вчити заново, бо ви все вже вмієте. А як радів роботодавець! Не треба наймати окремого розробника під кожну платформу, шоб зробити теж саме, але вже під айфон. Можна розробляти під всі платформи, які тоді можна було уявити: android, iPhone, Windows, BlackBerry (да, було таке), і навіть Nokia lumia з мобільним Windows. Яка чудова проривна технологія!
Це було так круто, шо в гру вступили серйозні гравці. Такі компанії, як IBM бачили великий потенціал в цій технології та розробляли свої рішення для збірки таких застосунків (Titanium)
Швидкість розробки вражала — один екран розроблявся в 3-5 разів швидше, ніж то робив нейтів-розробник. Заверстав стоінку, віддебажив прямо в хромі (а хром підтримував remode debug). І шо вражало що той екран вже відходив відразу під все. Під всі платформи. Ми радісно верстали сторінки, а замовники радісно потирали руки, бачучи готовий на 90 відсотків застосунок під всі на світи платформи.
Але залишалось доробити ще 10%. І от, коли вже всі прості екрани були зроблені, а задачі закриті тестувальниками, починались проблеми: внутрішні компоненти-браузери мали купу багів, причому на кожній платформі вони були свої (багато з них не вирішені й досі), анімації працювати дуже глючно, текстові поля перекривались клавіатурою і ніяк не хотіли потрапляти в фокус, деякі css-властивості не працювали, або працювали по різному на різних приладах, при скролі екрану виникали артефакти, іноді екрани просто не рендерились з першого разу, відображення великих списків вбивало перформанс до нуля, курсор текстового поля пробивався через всі div-и... З формами, де було багато полів для заповнення неможливо було працювати. А в десктопному хромі все працювало так добре (це все я знаю на власному досвіді). Тестувальники закидували нас новими багами, ми не встигали їх вигрібати, а вони знаходили ще і ще.
Єх, подумали розробники, от би всі оці div-и і span-и перетворювались би на нативні компоненти... Але такого не було. Натомість були спроби замінити рідний webview-компонент на кастомні збірки хрома (crosswalk), однак це приносило плюс декілька десятків мегабайт до застосунку і нові баги. Так той crosswalk і закрили потім, він вже давно не підтримується. Сама ж технологія потихеньку занепала. Cordova чи закинута, чи підтримується мінімально.
І тут зʼявляється react native — як друге дихання для фронтедщиків, які захотіли стати мобільними розробниками, але їх аплікухи погрязли в багах джири. Це була чудова технологія — компоненти рендерились нативно, але стек технологій залишався рідний і знайомий. Плавні анімації, рідні поля для вводу, нативний дизайн компонентів. Тепер все як у справжніх розробників під мобільні платформи. Щоправда, коло платформ трохи менше, але то не біда, бо не blackberry не Nokia Lumia вже давно нема. А андроїда і iOS цілком достатньо. Періодично (але вже рідше) зʼявляються статті про те, що реакт нейтів все ще ого-го і дасть фору нативним технологіям, ну або, як мінімум не гірше від них.
І от раптом шо? Виявляється, продуктивність react native недостатня, але це не біда, бо є чудовий і швидкий рендеринг складних анімацій в вбудованому браузері, який дійсно зробить вам шось круте і складне? Здається, десь таке вже було.

Дякую за коментар, є з чим погодитись в вашому дописі, але хочу зазначити, що React Native — це дійсно еволюція. Якщо Cordova була мобільною розробкою з акцентом на веб, то React Native — це мобільна розробка з акцентом на натив. Багато бібліотек пишуться на Swift і Kotlin, тому React Native розробники — це вже давно не «самозванці» в світі мобільної розробки. Вони створюють додатки, використовуючи ті ж самі нативні мови, коли в цьому є потреба, а коли ні — вони набагато швидші :)

І ця стаття не про використання браузера як такого, вона про можливість підняття сервера за допомогою ресурсів девайсу, Це можливість розробляти веб-додатки, а потім інтегрувати їх у мобільний додаток (як якусь окрему фічу) , дозволяючи працювати з ними офлайн

Як додати реакт в реакт, щоб використовувати реакт поки використовуєш реакт.
Не хочу здатися снобом, але якщо чесно, звучить як гайд «як робити не треба».

Іноді у вашому застосунку потрібно показати щось справді круте чи складне, наприклад гру чи графік, який рухається та змінюється. Зробити це в React Native може бути складно, і застосунок може працювати повільно.

Така потреба означає, що стек з самого початку було обрано не правильно, або хтось придумав вимоги, яких не варто було вимагати від поточного стеку. Це приблизно те ж саме, що змусити додаток на реакт нейтів працювати всередині айфрейму на вебі, бо «Іноді у вашому застосунку потрібно показати щось справді круте»

Цікаво, як часто ви змінюєте стек на проєкті, коли замовник змінює вимоги? Думати, що можна передбачити все — утопія. Іноді треба бути реалістами.

На думку одразу спадає декілька ситуацій коли це може бути корисно:
1) Коли потрібно реалізувати складний функціонал «на вчора».
2) Коли ресурси команди або компанії обмежені, і немає змоги або бажання наймати окремих Android та iOS розробників для реалізації невеликої, але складної для React Native фічі.
3) Коли просто треба перевірити фічу
4) Додати офлайн режим до аплікації

Мова не про те, що це ідеальне рішення або що воно краще за всі інші. Це просто опція, альтернатива між «зробити» і «не зробити». Як кажуть: «Done is better than perfect»

Коментар порушує правила спільноти і видалений модераторами.

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