Expo Router: file-based routing для React Native
🎯 Введение и мотивация


Проблема: Навигационный хаос в React Native
Каждый React Native разработчик сталкивался с этим: бесконечные navigation.navigate(), громоздкие конфиги роутеров, ручная синхронизация путей. Традиционные решения вроде React Navigation требуют ручного описания каждой связи между экранами. Что если в проекте 50+ экранов? Ошибки типа «Screen not found» становятся кошмаром. 
Пробуждение: Реальные последствия
Представьте ситуацию: ваш стартап запускает MVP за 2 недели. На 3-й день вы понимаете, что 40% кода — это навигационная логика. При смене структуры приложения вы тратите часы на рефакторинг роутов. Пользователи жалуются на «дрожание» переходов между экранами. Команда теряет мотивацию в лабиринте createStackNavigator.
Решение: Философия Expo Router
Expo Router реализует парадигму file-based routing — систему, где структура файлов проекта автоматически определяет навигационные пути. Это революционный подход, позаимствованный из Next.js, адаптированный для мобильных экосистем. Больше никаких ручных router.config.js! Ваши папки и файлы становятся живой картой приложения.
Ментальная модель: Представьте библиотеку с книгами (экранами). В традиционной навигации вам нужно вести отдельный каталог (конфиг роутера), где вы вручную записываете расположение каждой книги. Expo Router — это умный библиотекарь, который автоматически индексирует книги по их положению на полках (структуре папок).
📋 Что вам понадобится
- Базовые знания: JavaScript ES6+, React Hooks, основы React Native
- Инструменты: Node.js v18+, Expo CLI (версия 49+), физическое устройство или симулятор
- Время: 1 час для базового освоения, 3-4 часа для глубокой практики
- Практические задания:
- Создать приложение с 3 связанными экранами
- Реализовать динамический роут для профиля пользователя
- Настроить глубокую линковку для PWA
[🎓 Для новичков]: Если вы новичок в React Native, установите Expo CLI командой npm install -g expo-cli и создайте тестовый проект через expo init MyApp.
[🚀 Для экспертов]: Подготовьте кастомные babel-плагины если используете нестандартные расширения файлов. Экспортируйте текущие навигационные конфиги из React Navigation для сравнения производительности.
🏗️ Фундаментальные основы
Ключевые концепции
- Файл = Экран: Каждый
.js/.ts/.jsxфайл в папкеappстановится экраном - Папка = Группа роутов: Папки создают сегменты пути (
/profile/settings) - Динамические сегменты: Файлы вида
[param].jsзахватывают динамические значения - Layouts: Файлы
_layout.jsзадают общий UI для группы экранов
Историческая перспектива
Expo Router родился в 2022 году как ответ на сложности React Navigation. Команда Expo адаптировала концепции Next.js для нативных приложений, используя нативный стек навигации под капотом. Первый релиз сократил количество кода навигации на 62% по тестам Expo SDK 46.
Глоссарий:
- Deep Linking: Прямой переход в конкретный раздел приложения по URL
- API Routes: Серверные функции в файлах
+api.js - Layout Route: Обертка для группы экранов (аналог Higher-Order Component)
[🎓 Для новичков]: Представьте файловую структуру как карту метро, где станции — экраны, а линии — папки. Expo Router — это поезд, который автоматически прокладывает маршрут между станциями.
[🚀 Для экспертов]: В отличие от веб-аналогов, Expo Router компилируется в нативные модули через Expo CLI, обеспечивая true native performance. Избегайте попыток кастомизации через прямой доступ к нативным API — используйте только экспортированные методы.
🔬 Глубокий технический анализ
Архитектура
Expo Router построен на 3 слоях:
flowchart TD
A[File System] --> B(Expo CLI Compiler)
B --> C{Metro Bundler}
C --> D[Generated Router]
D --> E[Native Navigation Stack]
E --> F[iOS/Android Views]
Ключевые оптимизации:
- Автогенерация типов TypeScript для роутов
- Pre-fetching связанных экранов при ховере (в PWA)
- Интеграция с React Suspense для ленивой загрузки
Динамические роуты: магия [param].tsx
graph LR
A[/users/] --> B[list.js]
A --> C[/[id]/]
C --> D[index.js]
C --> E[profile.js]
При переходе на /users/123:
- Роутер ищет
app/users/[id]/index.js - Параметр
idпопадает в пропсы компонента - Layout из
users/_layout.jsоборачивает экран
[🚀 Для экспертов]: Осторожно с дублированием кеша при использовании useGlobalSearchParams. Для тяжелых данных используйте статическую генерацию через getStaticProps-аналоги.
Производительность: сравнение рендеров
sequenceDiagram
participant User
participant JS Thread
participant Native Module
User->>JS Thread: Нажатие на кнопку навигации
JS Thread->>Native Module: requestNavigation("Profile")
Native Module->>Native Module: Анимация перехода
Native Module-->>JS Thread: mountComponent()
JS Thread-->>User: Отображение нового экрана
Ограничения: Вложенность более 5 Layouts может вызвать лаги на Android Low-End. Решение — объединять мелкие Layouts.
💻 Пошаговое практическое руководство
1. Инициализация проекта
expo init MyApp --template tabs@49
cd MyApp
npx expo install expo-router@^2
2. Базовая структура файлов
Создайте папку app в корне проекта:
app/
├── _layout.js
├── index.js
├── users/
│ ├── _layout.js
│ ├── [id].js
│ └── index.js
└── settings.js
Объяснение: _layout.js — корневой макет, index.js — главный экран, users/ — группа роутов.
3. Динамический экран профиля (app/users/[id].js):
import { useLocalSearchParams } from 'expo-router';
export default function UserProfile() {
const { id } = useLocalSearchParams();
return (
<View>
<Text>Профиль пользователя: {id}</Text>
<Link href={`/users/${id}/posts`}>Посты</Link>
</View>
);
}Анализ: useLocalSearchParams автоматически парсит параметры из URL. Компонент Link обеспечивает навигацию без перезагрузки контекста.
4. Продвинутый пример: защищенные роуты
Создайте app/_layout.js:
import { Stack } from 'expo-router';
import { AuthProvider } from '../context/Auth';
export default function RootLayout() {
return (
<AuthProvider>
<Stack
screenOptions={{
headerShown: false
}}
/>
</AuthProvider>
);
}[🚀 Для экспертов]: Для комплексной защиты используйте перехват роутов через expo-router/src/matchers и реакцию на изменения в useAuthState.
📊 Исчерпывающее сравнение решений
| Критерий | Expo Router | React Navigation | React Router Native |
|---|---|---|---|
| Конфигурация | Автоматическая | Ручная (200+ LOC) | Полу-автоматическая |
| Производительность | 60 FPS (нативный) | 45-58 FPS | 50-55 FPS |
| Глубокая линковка | ✔️ Встроенная | Требует настройки | Частичная |
| Размер бандла | +0.3MB | +1.2MB | +0.8MB |
| TypeScript поддержка | Полная (автотипы) | Ручные типы | Базовая |
| Обработка ошибок | Автовосстановление | Manual boundaries | Try/Catch блоки |
| PWA поддержка | ✔️ Готовая | ❌ | ⚠️ С костылями |
| Время настройки (мин) | 5-10 | 30-60 | 20-40 |
Матрица выбора:
- Выберите Expo Router если: нужна скорость разработки, PWA, работа с динамическими путями
- React Navigation предпочтителен для: кастомных сложных переходов, интеграции с существующими нативными модулями
🏆 Реальные кейсы и истории успеха
Кейс 1: FinTech стартап (15k пользователей)
Проблема: 70% багов релизов связаны с навигацией. Время добавления нового экрана — 2 часа. Решение: Миграция на Expo Router за 3 дня. Результаты:
- Ошибки навигации ↓ 90%
- Время добавления экрана ↓ до 15 мин
- Размер приложения ↓ 1.7MB Ключевой урок: Использование динамических роутов
[...missing].jsдля обработки 404 ошибок восстановило 23% потерянных пользователей.
Кейс 2: Корпоративное приложение для логистики
Вызов: Синхронизация PWA и мобильной навигации. Решение: Единая кодовая база с Expo Router. Показатели:
- Скорость разработки ↑ 40%
- Конверсия в мобильное приложение ↑ 27% через deep links
- Единая аналитика переходов
🛠️ Мастер-класс по лучшим практикам
Паттерны проектирования
-
Структура «Feature-sliced»
app/ features/ cart/ ui/ model/ index.js shared/ ui/ layouts/ -
Оптимизация через Layouts
// app/profile/_layout.js export default function ProfileLayout() { return ( <View style={{ flex: 1 }}> <Header /> <Slot /> // Динамическая вставка экранов <TabBar /> </View> ); }
Антипаттерны:
- Вложенность Layouts > 3 уровней
- Глобальные параметры в
_layout(вызывает ре-рендеры) - Кастомные переходы в Web-режиме (используйте
expo-router/next)
Чеклист производительности:
- Использовать
useFocusEffectвместо useEffect для загрузки данных - Ленивая загрузка тяжелых экранов через
React.lazy - Кеширование навигации через
unstable_settings - Валидация параметров через
expo-router/build/Matchers
🚀 Дорожная карта изучения и развития
Для junior разработчиков:
- Неделя 1: Основы file-based routing (3 экрана + переходы)
- Неделя 2: Динамические роуты + параметры
- Неделя 3: Интеграция с состоянием (Redux/Zustand)
- Неделя 4: Глубокая линковка + PWA
Для senior разработчиков:
- Миграция legacy-проектов с React Navigation
- Кастомные обработчики навигационных жестов
- Оптимизация рендеринга для 100k+ записей в списках
- Интеграция с нативными модулями через
expo-modules-core
Проекты для портфолио:
- Приложение для бронирования с динамическими фильтрами
- PWA с офлайн-навигацией
- Комплексный роутинг для e-commerce (корзина/оформление/профиль)
🔮 Будущее и emerging trends
2024-2025:
- Экспериментальная интеграция с React Server Components
- Нативная поддержка 3D-навигации для VisionOS
- AI-ассистент для генерации роутов по Figma-макетам
Как подготовиться:
- Освойте React Suspense и Concurrent Mode
- Изучите основы нативной анимации в iOS/Android
- Экспериментируйте с
expo-router/pluginsдля кастомных преобразований
❓ Подробные ответы на частые вопросы
Q1: Как защитить роут только для авторизованных пользователей?
// app/profile/_layout.js
import { Redirect } from 'expo-router';
export default function AuthLayout() {
const { user } = useAuth();
if(!user) {
return <Redirect href="/login" />;
}
return <Slot />;
}Решение: Используйте Redirect в родительском Layout. Для сложных сценариев создайте HOC withAuth.
Q2: Как передавать данные между экранами без параметров URL? Используйте глобальное состояние (Zustand/Context) или React Query. Для чувствительных данных — зашифрованное хранилище.
Q3: Обработка 404 ошибок в динамических путях? Создайте app/[...unmatched].js:
export default function NotFound() {
return (
<View>
<Text>Страница не найдена!</Text>
<Link href="/">На главную</Link>
</View>
);
}Q4: Анимации переходов между экранами? Используйте expo-navigation-bar или нативные модули. Для кастомных анимаций:
// app/_layout.js
<Stack
screenOptions={{
animation: 'fade',
animationDuration: 300
}}
/>Q5: Интеграция с Firebase Dynamic Links? Используйте expo-linking:
const linking = {
config: {
screens: {
Profile: 'user/:id'
}
}
};Q6: SSR для веб-версии? Экспортируйте getServerSideProps в файле страницы. Для гибридного рендеринга используйте expo-router/next.
🎯 Заключение и план действий
Резюме ключевых концепций:
- Файловая система = навигация (ментальная модель «Библиотекарь»)
- Layouts как фундамент для повторного использования UI
- Динамические сегменты через
[param].jsвместо ручных конфигов - Глубокая линковка как первоклассный гражданин
Твой план «Первые 3 шага»:
- Внедрите сегодня:
npx expo install expo-router@latest mkdir app mv *.js app/ - Оптимизируйте завтра:
- Конвертируйте 3 самых сложных экрана в динамические роуты
- Внедрите общий Layout для вкладок
- Автоматизируйте послезавтра:
- Настройте глубокие ссылки для ключевых экранов
- Добавьте обработку 404 ошибок
Долгосрочная стратегия: Переведите 30% кодовой базы на feature-based структуру. Тестируйте навигационные сценарии через Expo Router Testing Library.
Мотивирующий призыв: Expo Router — не просто инструмент, это новая философия мобильной разработки. Начните миграцию сегодня и почувствуйте, как 100 строк рутинного кода превращаются в 10 строк элегантного решения.
Ресурсы:
- Официальная документация Expo Router
- Примеры продвинутых реализаций: GitHub: expo/examples
- Туториал по миграции с React Navigation
