React-Native性能优化

一、全局状态redux优化,避免相同值,引起常驻页面重复渲染

createSelector是redux-toolkit一个函数,它允许我们从Redux中选择部分状态数据,并将其作为参数传递给一个memoized函数,以避免在相同的输入下重复渲染。此外, createSelector还支持嵌套选择器和参数选择器,使得选择和组合Redux store中的状态更加灵活。

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

二、合并多个dispatch与setState,React框架会合并成一次渲染

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,
          }),
        );
      }

三、React.memo仅检查自己组件props变更,针对子组件不采用父组件的参数时,可减少子组件重复渲染

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 count={count}/> */}
            <ChildMemo/>
        </>
    )
}

四、useMemo仅在依赖项改变时才重新计算memoized值,避免每次渲染都进行高开销的计算,类似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></br>
            <div>
                {  `姓名:${info.name}  年龄:${info.age}  性别:${sex} `}
            </div>
            <Child info={info}></Child>
        </div>
    )
}

五、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} />
    </>
  );
}

六、dismissAll清理路由栈

我们有61个页面,其中4个常驻页面,57个非常驻页面,及时清理路由栈,避免路由循环,以及利用js回收机制回收内存。

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

七、常驻页面只缓存数据,不缓存dom

基于expo框架缓存常驻页面,离开页面时销毁dom,只保留数据。

const [isDomVisible, setIsDomVisible] = useState(true);

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

八、使用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}
    />

九、首页Stores列表,有特殊滑动需求,需使用BottomSheetFlatList虚拟列表

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

十、全局缓存设置,利用redux-persist实现全局状态持久化

使用redux-persist与AsyncStorage结合,每次状态变更存储在本地(需控制数据量),下次打开app优先使用本地缓存

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'],
      },
    }),
});

十一、接口缓存设置

利用AsyncStorage缓存主题、连锁店列表等数据,下次打开app优先使用本地缓存

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

十二、全局入口优化

严格控制全局路口_layout.tsx与tabs下_layout.tsx文件的重新渲染与io请求,避免所有页面可能引起的性能问题。

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();
  }, []);
posted @ 2024-09-25 16:40  jerry-mengjie  阅读(223)  评论(0)    收藏  举报