React Native应用性能优化实战技巧

Viewed 0

React Native应用性能优化实战技巧

在开发React Native应用时,性能优化是确保用户体验流畅的关键。本文将系统介绍一系列实战技巧,涵盖状态管理、组件渲染、列表处理、缓存策略等方面,帮助开发者避免常见性能陷阱,提升应用响应速度。

1. 全局状态Redux优化

使用Redux Toolkit的createSelector函数可以从Redux状态中选择特定数据,并通过memoization机制避免在相同输入下重复渲染。例如,创建一个认证状态选择器:

const isAuthenticatedSelector = createSelector(
  (state) => state.user,
  (user) => user.isAuthenticated,
);
const isAuthenticated = useSelector(isAuthenticatedSelector);

2. 合并多个Dispatch与setState

React框架会自动合并连续的setState调用,但对于dispatch操作,合理组织代码也能减少渲染次数。在更新用户信息时,可以合并多个dispatch:

dispatch(userActions.SET_THEME(themeData));
dispatch(userActions.updateOprInfo({ oprInfo }));
if (oprInfo.currency) {
  dispatch(i18nActions.updateCurrency({ currency: oprInfo.currency }));
}
if (oprInfo.currencySymbol) {
  dispatch(
    i18nActions.updateCurrencySymbol({
      currencySymbol: oprInfo.currencySymbol,
    }),
  );
}

3. 使用React.memo减少子组件渲染

React.memo仅检查组件props的变更。当子组件不依赖父组件的参数时,使用memo可以避免不必要的渲染:

import React, { useState, memo } from 'react';
const Child = (props) => {
  console.log('子组件渲染');
  return <div>子组件</div>;
};
const ChildMemo = memo(Child);
export default () => {
  const [count, setCount] = useState(0);
  return (
    <>
      <button onClick={(e) => { setCount(count + 1) }}>+</button>
      <p>计数器:{count}</p>
      <ChildMemo />
    </>
  );
};

4. 利用useMemo优化高开销计算

useMemo在依赖项改变时才重新计算值,类似于Vue的computed属性,避免每次渲染都进行重复计算:

import React, { useState, useMemo } from 'react';
const Child = function (props) {
  const { info } = { ...props };
  console.log(`子组件接收: ${info.age}`);
  return <div>显示子组件</div>;
};
export default () => {
  const [age, setAge] = useState(6);
  const [sex, setSex] = useState('boy');
  const info = useMemo(() => {
    return { name: 'echo', age: age };
  }, [age]);
  return (
    <div>
      <button onClick={() => { setAge(age => age + 1) }}>年龄+1</button>
      <button onClick={() => { setSex(sex => sex === 'boy' ? 'girl' : sex) }}>改变性别</button><br />
      <div>
        { `姓名:${info.name}  年龄:${info.age}  性别:${sex} `}
      </div>
      <Child info={info}></Child>
    </div>
  );
};

5. 使用useCallback避免函数重复创建

useCallback返回函数的memoized版本,防止每次渲染时重新创建函数,特别适用于传递给子组件的回调:

import React, { useState, useCallback, memo } from "react";
const Child = memo(function ({ onClick }) {
  console.log("子组件渲染");
  return <button onClick={onClick}>子组件</button>;
});
export default function Count() {
  const [name, setName] = useState(0);
  const [number, setNumber] = useState(0);
  const addClick = useCallback(() => {
    setNumber(number + 1);
  }, []);
  console.log("父组件渲染");
  return (
    <>
      <button onClick={() => setName(name + 1)}>父组件</button>
      <Child onClick={addClick} />
    </>
  );
}

6. 及时清理路由栈

在具有多个页面的应用中,使用Expo Router的dismissAll方法清理路由栈,可以避免路由循环和内存泄漏。例如,在切换到标签页时清理历史:

import { router } from 'expo-router';
function getScreen(screenId) {
  const tabs = ['home', 'topUp', 'order', 'me', 'login', 'profile', 'businessAccount'];
  if (tabs.includes(screenId)) {
    router.dismissAll();
  }
}

7. 常驻页面只缓存数据,不缓存DOM

基于Expo框架,可以在页面离开时销毁DOM,仅保留数据,以减少内存占用。通过useFocusEffect控制DOM可见性:

const [isDomVisible, setIsDomVisible] = useState(true);
useFocusEffect(
  useCallback(() => {
    setIsDomVisible(true);
    return () => {
      setIsDomVisible(false);
    };
  }, []),
);
return isDomVisible ? <GestureHandlerRootView /> : <Loading isLoading={true} />;

8. 使用FlatList虚拟列表处理长数据

对于长列表,应使用FlatList替代ScrollView,实现虚拟滚动以提升性能。例如,在订单列表中:

import IGFlatList from '@/CustomElements/IGFlatList';
<IGFlatList
  data={orders}
  loading={loading}
  hasMoreData={hasMoreData}
  handleLoadMore={handleLoadMore}
  renderItem={ListItem}
  keyExtractor={(item) => item.orderNo.toString()}
  scrollToTopVar={scrollToTopVar}
/>

9. 特殊滑动需求使用BottomSheetFlatList

当列表需要与底部抽屉结合时,例如商店列表,使用BottomSheetFlatList

import BottomSheet, { BottomSheetFlatList } from '@gorhom/bottom-sheet';
<BottomSheetFlatList
  data={storesData}
  keyExtractor={(item) => item.staPkId.toString()}
  renderItem={renderItem}
/>

10. 全局状态持久化

使用redux-persist与AsyncStorage结合,实现Redux状态的持久化存储,确保应用重启后状态恢复:

import { persistStore, persistReducer } from 'redux-persist';
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE', 'persist/PAUSE', 'persist/PURGE', 'persist/REGISTER'],
      },
    }),
});

11. 接口缓存设置

利用AsyncStorage缓存常用数据,如主题和连锁店列表,减少网络请求,提升加载速度:

export const setInitStores = async (value) => {
  try {
    await AsyncStorage.setItem('initStores', JSON.stringify(value));
  } catch (error) {
    console.error('Error setting initStores:', error);
  }
};

12. 全局入口优化

在应用的全局入口文件(如_layout.tsx)中,应严格控制重新渲染和IO请求,避免影响所有页面性能。通过useCallbackuseEffect组织初始化逻辑:

const globalRequest = useCallback(async () => {
  try {
    dispatch(userActions.updateOprPkId({ oprPkId }));
    const localTheme = await storageGetTheme();
    if (localTheme) {
      setTheme(handleTheme(localTheme));
      setThemeReady(true);
    } else {
      setIsLoading(true);
    }
    const [oprInfoRes, themeRes] = await Promise.all([getOperInfo(), getTheme()]);
    const { user } = store.getState();
    if (user.token) {
      const [accountRes] = await Promise.all([getAccountBaseInfo()]);
      const accountInfo = accountRes.data.data[0];
      dispatch(userActions.updateAccountInfo({ accountInfo }));
    }
    const oprInfo = oprInfoRes.data.data[0];
    const themeData = themeRes.data.data[0];
    setTheme(handleTheme(themeData));
    setThemeReady(true);
    storageSetTheme(themeData);
    dispatch(userActions.SET_THEME(themeData));
    dispatch(userActions.updateOprInfo({ oprInfo }));
    if (oprInfo.currency) {
      dispatch(i18nActions.updateCurrency({ currency: oprInfo.currency }));
    }
    if (oprInfo.currencySymbol) {
      dispatch(
        i18nActions.updateCurrencySymbol({
          currencySymbol: oprInfo.currencySymbol,
        }),
      );
    }
  } catch (error) {
    console.error('Error globalRequest', error);
    ToastManager.show(t('networkError'));
    setTheme(handleTheme(await storageGetTheme()));
  } finally {
    setIsLoading(false);
    setThemeReady(true);
  }
}, []);
useEffect(() => {
  globalRequest();
}, []);

通过综合应用以上技巧,开发者可以有效提升React Native应用的性能,减少不必要的渲染和内存占用,确保用户体验流畅且高效。

0 Answers