Как реализовать lazy loading экранов?
Lazy loading (ленивая загрузка) экранов в React Native позволяет откладывать загрузку компонентов до тех пор, пока они не понадобятся — например, до момента навигации на экран. Это значительно улучшает производительность приложения, особенно при большом числе экранов или тяжёлых компонентов. Основная идея — загружать экраны динамически, а не сразу при старте приложения.
В React Native ленивую загрузку чаще всего реализуют при помощи:
-
React.lazy() и Suspense (в RN Web и RN с React 18+)
-
асинхронного импорта экранов в навигации (например, с помощью createNativeStackNavigator из react-navigation)
-
кастомных механизмов, например, отображения заглушки (loader/spinner), пока не загрузится экран
Основы: что такое React.lazy()
React.lazy() — это функция из React, которая позволяет динамически загружать компоненты. Компонент загружается только тогда, когда он впервые используется.
const ProfileScreen = React.lazy(() => import('./screens/ProfileScreen'));
Чтобы использовать такие компоненты, нужно обернуть их в Suspense:
import { Suspense } from 'react';
<Suspense fallback={<Text>Загрузка...</Text>}>
<ProfileScreen />
</Suspense>
Однако в React Native Suspense официально поддерживается только для некоторых случаев (например, при использовании React 18 и concurrent features). Поэтому в большинстве production-проектов применяют ленивую загрузку экранов в контексте react-navigation, без Suspense.
Lazy loading с react-navigation
1. Установка необходимых пакетов
npm install @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context react-native-gesture-handler
2. Базовая структура навигации с ленивой загрузкой
React Navigation автоматически поддерживает отложенную инициализацию экранов, если использовать getComponent.
App.js
import \* as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" getComponent={() => require('./screens/HomeScreen').default} />
<Stack.Screen name="Profile" getComponent={() => require('./screens/ProfileScreen').default} />
</Stack.Navigator>
</NavigationContainer>
);
}
getComponent загружает компонент только при первом открытии экрана, в отличие от component, который импортируется сразу.
Отличие между component и getComponent
Свойство | component={...} | getComponent={() => require(...)} |
---|---|---|
Время загрузки | Сразу при запуске | Только при первом использовании |
--- | --- | --- |
Производительность | Медленнее, особенно с большим числом экранов | Быстрее при старте, меньше памяти |
--- | --- | --- |
Подходит для lazy | ❌ | ✅ |
--- | --- | --- |
Пример плохой практики (без lazy loading)
import HomeScreen from './screens/HomeScreen';
import ProfileScreen from './screens/ProfileScreen';
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
В этом случае оба экрана будут загружены в память при старте приложения, даже если пользователь не откроет ProfileScreen.
Пример хорошей практики (с ленивой загрузкой)
<Stack.Navigator>
<Stack.Screen
name="Home"
getComponent={() => require('./screens/HomeScreen').default}
/>
<Stack.Screen
name="Profile"
getComponent={() => require('./screens/ProfileScreen').default}
/>
</Stack.Navigator>
Комбинирование с React.lazy() и Suspense
Если вы используете React 18, можно сделать гибридную реализацию:
const ProfileScreen = React.lazy(() => import('./screens/ProfileScreen'));
<Stack.Screen
name="Profile"
children={() => (
<Suspense fallback={<Text>Загрузка...</Text>}>
<ProfileScreen />
</Suspense>
)}
/>
Однако Suspense не поддерживается во всех версиях React Native, и поведение может отличаться в зависимости от платформы (iOS, Android, Web).
Дополнительная оптимизация: код-сплитинг
Использование динамического импорта вне навигации
const loadComponent = async () => {
const module = await import('./screens/HeavyComponent');
return module.default;
};
const \[Component, setComponent\] = useState(null);
useEffect(() => {
loadComponent().then(setComponent);
}, \[\]);
Это позволяет вручную загружать компоненты по событию (например, по нажатию на кнопку).
Использование Expo с ленивой загрузкой
Expo поддерживает lazy loading аналогично обычному React Native-проекту. Главное — избегать предзагрузки всех экранов в import. Пример:
<Stack.Screen
name="Settings"
getComponent={() => require('../screens/SettingsScreen').default}
/>
Поддержка отложенной отрисовки (lazy rendering)
Помимо отложенной загрузки, можно использовать отложенную отрисовку (например, внутри табов):
<Tab.Navigator screenOptions={{ lazy: true }}>
<Tab.Screen name="Feed" component={FeedScreen} />
<Tab.Screen name="Notifications" component={NotificationsScreen} />
</Tab.Navigator>
Параметр lazy: true означает, что экран не будет отрисован до тех пор, пока пользователь не перейдёт на него.
Предупреждения и ограничения
-
getComponent нельзя использовать вместе с component, только один из них
-
require() должен использовать абсолютный или относительный путь, а не переменные
-
Lazy loading не работает, если вы делаете статический импорт всех экранов в начале
-
Suspense пока не работает на 100% в React Native, особенно без React 18
Поддержка библиотеки react-native-screens
Если используется react-native-screens, то рекомендуется вызвать enableScreens() для оптимизации навигации и памяти:
import { enableScreens } from 'react-native-screens';
enableScreens();
Эта библиотека помогает уменьшить использование памяти путём размонтирования неактивных экранов и управления их жизненным циклом.
Ленивая загрузка экранов в React Native — это эффективная техника оптимизации, особенно в приложениях с множеством экранов или тяжёлыми модулями. Наиболее надёжный способ — использовать getComponent из react-navigation, который встроенно поддерживает ленивую загрузку. Это улучшает скорость старта приложения и экономит ресурсы устройства, снижая нагрузку на память и ускоряя навигацию.