首頁  >  文章  >  web前端  >  掌握 React Native/ReactJS 中的延遲載入概念

掌握 React Native/ReactJS 中的延遲載入概念

DDD
DDD原創
2024-09-18 15:27:49889瀏覽

延遲載入是一種僅在需要時才載入資源的設計模式。這有利於改善 React Native 應用程式的初始載入時間,減少記憶體消耗,提高整體效能。

Master lazy loading concept in React Native/ReactJS

為什麼要延遲載入?

  1. 效能最佳化:防止應用程式首次啟動時載入不必要的資源,從而顯著減少載入時間。
  2. 減少記憶體使用:延遲載入可防止在不需要時將映像、元件或外部函式庫等大型資源保留在記憶體中。
  3. 改進的使用者體驗:按需載入資源提供更流暢的導航和互動。

1) React Native 螢幕/元件中延遲載入的用例

  1. 延遲載入畫面(程式碼分割):
    在 React Native 中,延遲載入通常用於元件,特別是當您有用戶可能不經常存取的不同螢幕時。透過延遲載入這些螢幕,您可以減小初始包的大小。

  2. 使用 React.lazy() 和 Suspense 進行延遲載入:
    React 引入了 React.lazy() 函數來啟用元件的延遲載入。若要使用延遲加載,可使用 Suspense 作為後備,直到元件加載。

正常使用

在正常使用中,所有資源、元件、程式庫和資料都會在應用程式啟動時預先載入。這種方法適用於小型應用程序,但隨著應用程式的成長,可能會變得效率低下且資源密集,從而影響效能和載入時間。

範例:正常組件加載

import React from 'react';
import HomeScreen from './screens/HomeScreen';
import ProfileScreen from './screens/ProfileScreen';

const App = () => {
  return (
    <>
      <HomeScreen />
      <ProfileScreen />
    </>
  );
};

export default App;

說明

  • 在此範例中,HomeScreen 和 ProfileScreen 元件都是預先匯入和載入的,無論使用者是否導航到它們。
  • 這會增加初始載入時間,因為所有元件都會立即捆綁並載入。

延遲載入用法

透過延遲加載,元件、庫或資料僅在需要時加載。由於僅按需載入必要的資源,因此可以透過減少初始載入時間和記憶體使用量來提高效能。

範例:延遲載入元件

import React, { Suspense, lazy } from 'react';
import { ActivityIndicator } from 'react-native';

const HomeScreen = lazy(() => import('./screens/HomeScreen'));
const ProfileScreen = lazy(() => import('./screens/ProfileScreen'));

const App = () => {
  return (
    <Suspense fallback={<ActivityIndicator size="large" color="#0000ff" />}>
      <HomeScreen />
      <ProfileScreen />
    </Suspense>
  );
};

export default App;

說明

  • 在此範例中,HomeScreen 和 ProfileScreen 是使用 React.lazy() 延遲載入的。
  • 在載入這些元件之前,會顯示後備(ActivityIndi​​cator)。
  • 這減少了應用程式的初始載入時間,因為元件僅在渲染時載入。

正常載入和延遲載入之間的差異

功能 正常使用 延遲載入 標題>
Feature Normal Usage Lazy Loading
Loading Strategy Everything is loaded upfront when the app starts. Components, resources, or data are loaded only when needed.
Initial Load Time Higher, as all resources are loaded at once. Lower, as only essential components are loaded upfront.
Memory Usage Higher, as all components and resources are loaded into memory. Lower, as only necessary components are loaded into memory.
User Experience Slower startup but smoother transitions once loaded. Faster startup but slight delay when loading resources.
Best for Small applications with limited components. Large applications where not all components are used initially.
Implementation Simpler, as everything is bundled at once. Requires managing dynamic imports and possibly loading states.
載入策略 應用程式啟動時,所有內容都會預先載入。 僅在需要時載入元件、資源或資料。 初始載入時間 更高,因為所有資源都會立即載入。 較低,因為僅預先載入必要的組件。 記憶體使用量 更高,因為所有元件和資源都載入到記憶體中。 較低,因為僅將必要的組件載入到記憶體中。 使用者體驗 啟動速度較慢,但載入後過渡更平滑。 啟動速度更快,但載入資源時略有延遲。 最適合 組件有限的小型應用程式。 最初並未使用所有組件的大型應用程式。 實作 更簡單,因為所有內容都一次捆綁在一起。 需要管理動態導入和可能的載入狀態。 表>

2. Lazy Loading in Navigation (React Navigation):

Lazy loading ensures that screens or components are only mounted when they are accessed (when the user navigates to them), thus improving performance, especially in apps with multiple screens.

Example: Lazy Loading in React Navigation (Stack Navigator)

import React, { Suspense, lazy } from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native';
import { ActivityIndicator } from 'react-native';

// Lazy load screens
const HomeScreen = lazy(() => import('./screens/HomeScreen'));
const ProfileScreen = lazy(() => import('./screens/ProfileScreen'));

const Stack = createStackNavigator();

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen
          name="Home"
          component={() => (
            <Suspense fallback={<ActivityIndicator size="large" color="#0000ff" />}>
              <HomeScreen />
            </Suspense>
          )}
        />
        <Stack.Screen
          name="Profile"
          component={() => (
            <Suspense fallback={<ActivityIndicator size="large" color="#0000ff" />}>
              <ProfileScreen />
            </Suspense>
          )}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

export default App;

Explanation:

  • In this example, the HomeScreen and ProfileScreen components are lazily loaded using React.lazy().
  • The Suspense component provides a loading indicator (ActivityIndicator) while the components are being loaded.
  • Screens will only load when the user navigates to them, reducing the initial load time.

3. Lazy Loading Images :

In React Native, lazy loading can be achieved using libraries like react-native-fast-image or manually handling image loading by tracking visibility with tools like IntersectionObserver.

a) Using react-native-fast-image

react-native-fast-image is a performant image component that provides built-in lazy loading.

npm install react-native-fast-image

Example: Lazy Loading Images with react-native-fast-image

import React from 'react';
import { View, ScrollView, Text } from 'react-native';
import FastImage from 'react-native-fast-image';

const LazyLoadingImages = () => {
  return (
    <ScrollView>
      <Text>Scroll down to load images</Text>
      <FastImage
        style={{ width: 200, height: 200 }}
        source={{
          uri: 'https://example.com/my-image1.jpg',
          priority: FastImage.priority.normal,
        }}
        resizeMode={FastImage.resizeMode.contain}
      />
      <FastImage
        style={{ width: 200, height: 200 }}
        source={{
          uri: 'https://example.com/my-image2.jpg',
          priority: FastImage.priority.normal,
        }}
        resizeMode={FastImage.resizeMode.contain}
      />
    </ScrollView>
  );
};

export default LazyLoadingImages;

Explanation:

  • The FastImage component from react-native-fast-image helps with lazy loading. It loads images only when they are about to be displayed.
  • It also provides efficient caching and priority options, improving performance.

b) Manual Lazy Loading (Visibility Tracking)

In cases where you don't want to use a third-party library, you can implement lazy loading by tracking when an image enters the viewport using tools like IntersectionObserver (web) or a custom scroll listener in React Native.

Example: Lazy Loading with Visibility Tracking (using React Native)

import React, { useState, useEffect } from 'react';
import { View, Image, ScrollView } from 'react-native';

const LazyImage = ({ src, style }) => {
  const [isVisible, setIsVisible] = useState(false);

  const onScroll = (event) => {
    // Implement logic to determine if image is visible based on scroll position
    const { y } = event.nativeEvent.contentOffset;
    if (y > 100) { // Example: load image when scrolled past 100px
      setIsVisible(true);
    }
  };

  return (
    <ScrollView onScroll={onScroll} scrollEventThrottle={16}>
      <View>
        {isVisible ? (
          <Image source={{ uri: src }} style={style} />
        ) : (
          <View style={style} />
        )}
      </View>
    </ScrollView>
  );
};

const App = () => {
  return (
    <LazyImage src="https://example.com/my-image.jpg" style={{ width: 200, height: 200 }} />
  );
};

export default App;

Explanation:

  • The LazyImage component only loads the image once the user has scrolled a certain amount (onScroll event).
  • This approach manually tracks the scroll position and loads the image accordingly.

4. Lazy Loading with Redux (Dynamic Reducers) :

When using Redux, you may want to lazy load certain reducers only when necessary, such as for specific screens or features.

  1. Create a function to inject reducers dynamically.
  2. Add the new reducer to the Redux store when needed (e.g., when a user navigates to a new screen).
  3. Remove the reducer when it is no longer needed (optional).

Example: Lazy Loading Reducers in a React Application with Redux

1. Initial Redux Store Setup

Start by setting up a standard Redux store, but instead of adding all reducers upfront, create an injection method.

import { configureStore, combineReducers } from '@reduxjs/toolkit';

const staticReducers = {
  // Add reducers that are needed from the start
};

export const createReducer = (asyncReducers = {}) => {
  return combineReducers({
    ...staticReducers,
    ...asyncReducers,
  });
};

const store = configureStore({
  reducer: createReducer(),
});

// Store injected reducers here
store.asyncReducers = {};

export default store;

In the above code:

  • staticReducers: Contains reducers that are loaded when the app starts.
  • asyncReducers: This object will contain dynamically injected reducers as they are loaded.

2. Dynamic Reducer Injection Method

Create a helper function to inject new reducers dynamically into the store.

// Helper function to inject a new reducer dynamically
export function injectReducer(key, asyncReducer) {
  if (!store.asyncReducers[key]) {
    store.asyncReducers[key] = asyncReducer;
    store.replaceReducer(createReducer(store.asyncReducers));
  }
}

The injectReducer function checks if a reducer has already been added. If not, it injects it into the store and replaces the current root reducer.

3. Loading Reducer When Needed (Lazy Loading)

Imagine you have a new page or feature that needs its own reducer. You can inject the reducer dynamically when this page is loaded.

import { lazy, Suspense, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { injectReducer } from './store';
import featureReducer from './features/featureSlice'; // The reducer for this feature

const FeatureComponent = lazy(() => import('./components/FeatureComponent'));

const FeaturePage = () => {
  const dispatch = useDispatch();

  useEffect(() => {
    injectReducer('feature', featureReducer); // Dynamically load the reducer
  }, [dispatch]);

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <FeatureComponent />
    </Suspense>
  );
};

export default FeaturePage;

Here:

  • When the FeaturePage component is loaded, the injectReducer function adds the featureReducer dynamically to the Redux store.
  • The Suspense component handles lazy loading of the FeatureComponent.

4. Feature Reducer Example

The reducer for the feature is written as usual, using Redux Toolkit.

import { createSlice } from '@reduxjs/toolkit';

const featureSlice = createSlice({
  name: 'feature',
  initialState: { data: [] },
  reducers: {
    setData: (state, action) => {
      state.data = action.payload;
    },
  },
});

export const { setData } = featureSlice.actions;
export default featureSlice.reducer;

Removing a Reducer (Optional)

You might want to remove a reducer when it's no longer needed, for example, when navigating away from a page.

Here’s how you can remove a reducer:

export function removeReducer(key) {
  if (store.asyncReducers[key]) {
    delete store.asyncReducers[key];
    store.replaceReducer(createReducer(store.asyncReducers));
  }
}

You can call this function when a feature or page is unmounted to remove its reducer from the store.


5. Lazy Loading Libraries/Packages:

If you are using heavy third-party libraries, lazy loading them can help optimize performance.

   import React, { useState } from 'react';

   const HeavyComponent = React.lazy(() => import('heavy-library')); // React.lazy(() => import('moment'))

   const App = () => {
     const [showComponent, setShowComponent] = useState(false);

     return (
       <View>
         <Button title="Load Heavy Component" onPress={() => setShowComponent(true)} />
         {showComponent && (
           <Suspense fallback={<Text>Loading...</Text>}>
             <HeavyComponent />
           </Suspense>
         )}
       </View>
     );
   };
  1. Lazy Loading Data: You can also implement lazy loading for data fetching, where data is fetched in chunks or when a user scrolls (infinite scroll).

Example: Lazy Loading Data:

   import React, { useState, useEffect } from 'react';
   import { FlatList, ActivityIndicator, Text } from 'react-native';

   const LazyLoadData = () => {
     const [data, setData] = useState([]);
     const [loading, setLoading] = useState(true);

     useEffect(() => {
       fetch('https://api.example.com/data')
         .then(response => response.json())
         .then(json => {
           setData(json);
           setLoading(false);
         });
     }, []);

     if (loading) {
       return <ActivityIndicator />;
     }

     return (
       <FlatList
         data={data}
         renderItem={({ item }) => <Text>{item.name}</Text>}
         keyExtractor={item => item.id}
       />
     );
   };

   export default LazyLoadData;

Explanation:

  • Fetching data lazily ensures that the app doesn’t load too much data at once, improving performance and reducing bandwidth usage.

Summary of Use Cases:

  1. Screens/Components: Lazy loading React Native screens or components using React.lazy() and Suspense.
  2. Images: Lazy loading images as they enter the viewport to save memory and bandwidth.
  3. Redux Reducers: Injecting reducers dynamically to reduce the initial store size in Redux.
  4. Libraries: Lazily load third-party libraries or packages to reduce the initial bundle size.
  5. Data: Implement lazy loading for large datasets using techniques like pagination or infinite scrolling.

Lazy loading helps improve the performance, memory usage, and user experience of your React Native app, making it more efficient and responsive for users.

以上是掌握 React Native/ReactJS 中的延遲載入概念的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn