1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        React Hooks 性能優(yōu)化的正確姿勢

        共 4081字,需瀏覽 9分鐘

         ·

        2021-12-01 11:59

        前言

        React Hooks?出來很長一段時間了,相信有不少的朋友已經(jīng)深度使用了。無論是?React?本身,還是其生態(tài)中,都在摸索著進(jìn)步。鑒于我使用的?React?的經(jīng)驗,給大家分享一下。對于?React hooks,性能優(yōu)化可以從以下幾個方面著手考慮。

        場景1

        在使用了?React Hooks?后,很多人都會抱怨渲染的次數(shù)變多了。沒錯,官方就是這么推薦的:

        我們推薦把?state?切分成多個?state?變量,每個變量包含的不同值會在同時發(fā)生變化。

        function Box() {  const [position, setPosition] = useState({ left: 0, top: 0 });  const [size, setSize] = useState({ width: 100, height: 100 });  // ...}

        這種寫法在異步的條件下,比如調(diào)用接口返回后,同時?setPosition?和?setSize,相較于 class 寫法會額外多出一次?render。這也就是渲染次數(shù)變多的根本原因。當(dāng)然這種寫法仍然值得推薦,可讀性和可維護(hù)性更高,能更好的邏輯分離。

        針對這種場景若出現(xiàn)十幾或幾十個?useState?的時候,可讀性就會變差,這個時候就需要相關(guān)性的組件化了。以邏輯為導(dǎo)向,抽離在不同的文件中,借助?React.memo?來屏蔽其他?state?導(dǎo)致的?rerender

        const Position = React.memo(({ position }: PositionProps) => {  // position 相關(guān)邏輯  return (    
        {position.left}
        );});

        因此在?React hooks?組件中盡量不要寫流水線代碼,保持在 200 行左右最佳,通過組件化降低耦合和復(fù)雜度,還能優(yōu)化一定的性能。

        場景2

        class?對比?hooks,上代碼:

        class Counter extends React.Component {  state = {    count: 0,  };  increment = () => {    this.setState((prev) => ({      count: prev.count + 1,    }));  };  render() {    const { count } = this.state;    return ;  }}
        function Counter() {  const [count, setCount] = React.useState(0);  function increment() {    setCount((n) => n + 1);  }  return ;}

        憑直觀感受,你是否會覺得?hooks?等同于?class?的寫法?錯,hooks?的寫法已經(jīng)埋了一個坑。在?count?狀態(tài)更新的時候,?Counter?組件會重新執(zhí)行,這個時候會重新創(chuàng)建一個新的函數(shù)?increment。這樣傳遞給?ChildComponent?的?onClick?每次都是一個新的函數(shù),從而導(dǎo)致?ChildComponent?組件的?React.memo?失效。

        解決辦法:

        function usePersistFn any>(fn: T) {  const ref = React.useRef(() => {    throw new Error('Cannot call function while rendering.');  });  ref.current = fn;  return React.useCallback(ref.current as T, [ref]);}
        // 建議使用 `usePersistFn`const increment = usePersistFn(() => {  setCount((n) => n + 1);});// 或者使用 useCallbackconst increment = React.useCallback(() => {  setCount((n) => n + 1);}, []);

        上面聲明了?usePersistFn?自定義?hook,可以保證函數(shù)地址在本組件中永遠(yuǎn)不會變化。完美解決?useCallback?依賴值變化而重新生成新函數(shù)的問題,邏輯量大的組件強(qiáng)烈建議使用。

        不僅僅是函數(shù),比如每次?render?所創(chuàng)建的新對象,傳遞給子組件都會有此類問題。盡量不在組件的參數(shù)上傳遞因?render?而創(chuàng)建的對象,比如?style={{ width: 0 }}?此類的代碼用?React.useMemo?或?React.memo?編寫?equal?函數(shù)來優(yōu)化。

        style?若不需改變,可以提取到組件外面聲明。盡管這樣做寫法感覺太繁瑣,但是不依賴?React.memo?重新實現(xiàn)的情況下,是優(yōu)化性能的有效手段。

        const style: React.CSSProperties = { width: 100 };function CustomComponent() {  return ;}

        場景3

        對于復(fù)雜的場景,使用?useWhyDidYouUpdate?hook 來調(diào)試當(dāng)前的可變變量引起的?rerender。這個函數(shù)也可直接使用?ahooks?中的實現(xiàn)。

        function useWhyDidYouUpdate(name, props) {  const previousProps = useRef();  useEffect(() => {    if (previousProps.current) {      const allKeys = Object.keys({ ...previousProps.current, ...props });      const changesObj = {};      allKeys.forEach(key => {        if (previousProps.current[key] !== props[key]) {          changesObj[key] = {            from: previousProps.current[key],            to: props[key]          };        }      });      if (Object.keys(changesObj).length) {        console.log('[why-did-you-update]', name, changesObj);      }    }    previousProps.current = props;  });}const Counter = React.memo(props => {  useWhyDidYouUpdate('Counter', props);  return 
        {props.count}
        ;
        });

        當(dāng)?useWhyDidYouUpdate?中所監(jiān)聽的 props 發(fā)生了變化,則會打印對應(yīng)的值對比,是調(diào)試中的神器,極力推薦。

        場景4

        借助 Chrome Performance 代碼進(jìn)行調(diào)試,錄制一段操作,在?Timings?選項卡中分析耗時最長邏輯在什么地方,會展現(xiàn)出組件的層級棧,然后精準(zhǔn)優(yōu)化。

        場景5

        在?React?中是極力推薦函數(shù)式編程,可以讓數(shù)據(jù)不可變性作為我們優(yōu)化的手段。我在?React?class 時代大量使用了?immutable.js?結(jié)合?redux?來搭建業(yè)務(wù),與?React?中?PureComponnet?完美配合,性能保持非常好。但是在?React hooks?中再結(jié)合?typescript?它就顯得有點(diǎn)格格不入了,類型支持得不是很完美。這里可以嘗試一下?immer.js,引入成本小,寫法也簡潔了不少。

        const nextState = produce(currentState, (draft) => {  draft.p.x.push(2);}) // truecurrentState === nextState;

        場景6

        復(fù)雜場景使用?Map?對象代替數(shù)組操作,map.get(),?map.has(),與數(shù)組查找相比尤其高效。

        // Mapconst map = new Map([['a', { id: 'a' }], ['b', { id: 'b' }], ['c', { id: 'c' }]]);// 查找值map.has('a');// 獲取值map.get('a');// 遍歷map.forEach(n => n);// 它可以很容易轉(zhuǎn)換為數(shù)組Array.from(map.values());// 數(shù)組const list = [{ id: 'a' }, { id: 'b' }, { id: 'c' }];// 查找值list.some(n => n.id === 'a');// 獲取值list.find(n => n.id === 'a');// 遍歷list.forEach(n => n);

        結(jié)語

        React 性能調(diào)優(yōu),除了阻止?rerender,還有與寫代碼的方式有關(guān)系。最后,我要推一下近期寫的?React?狀態(tài)管理庫 https://github.com/MinJieLiu/heo,也可以作為性能優(yōu)化的一個手段,希望大家從?redux?的繁瑣中解放出來,省下的時間用來享受生活??


        瀏覽 43
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            在线视频亚洲 | AV无码观看| 激情五月天中文字幕 | 国产黄色小说在线观看 | 国产女人囗交视频 | AV电影免费观看 | 色欲影视综合插插网 | 国产又粗又硬的视频 | 69视频网站 | 狠狠色噜噜噜狠狠 |