学习笔记──React──性能优化

常见的性能优化策略

1. 使用 React.memo 优化函数组件

React.memo 是一个高阶组件,可以缓存组件的输出结果,从而避免不必要的重新渲染。它类似于类组件中的 shouldComponentUpdate。它通过对比前后两次的 props,如果 props 没有变化,则跳过重新渲染组件。

用法

import React from 'react';
const MyComponent = React.memo(function MyComponent(props) {
  /* 渲染逻辑 */
});

示例

import React, { useState } from 'react';
const Child = React.memo(({ value }) => {
  console.log('Child component rendered');
  return <div>{value}</div>;
});
const Parent = () => {
  const [count, setCount] = useState(0);
  const [value, setValue] = useState('Hello');

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <Child value={value} />
    </div>
  );
};
export default Parent;

在上述示例中,点击按钮仅会重新渲染 Parent 组件,而不会重新渲染 Child 组件,因为 Child 的 props (value) 没有变化。

2. 使用 useCallbackuseMemo

在函数组件中,可以使用 useCallbackuseMemo 来优化性能。

useCallback 是一个Hook,用于缓存函数。每次组件重新渲染时,useCallback 返回的函数只有在依赖项改变时才会更新。这对于将回调函数传递给子组件是非常有用的,因为它可以防止不必要的子组件重新渲染。

useMemo 是一个Hook,用于缓存计算结果。它会在依赖项改变时重新计算值,在依赖项不变时返回缓存的值。适用于昂贵的计算,避免每次渲染都进行重复计算。

可以组合使用,但它们并不是必须要一起使用。它们各自解决不同的问题,但在某些情况下,组合使用它们可以更有效地优化性能。

import React, { useState, useMemo, useCallback } from 'react';
const Child = React.memo(({ onClick, value }) => {
  console.log('Child rendered');
  return (
    <div>
      <button onClick={onClick}>Click me</button>
      <div>{value}</div>
    </div>
  );
});
const Parent = () => {
  const [count, setCount] = useState(0);
  const expensiveValue = useMemo(() => {
    console.log('Computing expensive value');
    return count * 2;
  }, [count]);
  const handleClick = useCallback(() => {
    console.log('Button clicked');
  }, []);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <Child onClick={handleClick} value={expensiveValue} />
    </div>
  );
};
export default Parent;

在这个例子中,expensiveValue 是通过 useMemo 缓存的计算结果,而 handleClick 是通过 useCallback 缓存的函数。两者结合使用可以确保 Child 组件在 count 变化时只重新渲染一次。

3. 使用 PureComponent 优化类组件

React.PureComponentReact.Component 的一个变种。它通过浅比较 propsstate 来实现 shouldComponentUpdate 的自动化,从而减少不必要的重新渲染。

import React from 'react';
class MyComponent extends React.PureComponent {
  /* 渲染逻辑 */
}

4. 避免匿名函数和对象

在渲染过程中创建匿名函数和对象会导致组件重新渲染。可以将这些函数和对象提取到组件外部,或者使用 useCallbackuseMemo

const MyComponent = () => {
  const handleClick = () => {
    console.log('Clicked!');
  };
  const style = {
    color: 'red',
  };
  return (
    <button onClick={handleClick} style={style}>Click me</button>
  );
};

5. 分割代码

使用动态导入和 React.lazy 来实现代码分割,仅加载当前页面所需的代码,减少初始加载时间。

import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const MyComponent = () => (
  <Suspense fallback={<div>Loading...</div>}>
    <OtherComponent />
  </Suspense>
);

6. 使用 shouldComponentUpdategetSnapshotBeforeUpdate

在类组件中,可以通过 shouldComponentUpdate 来手动控制组件是否需要重新渲染。

class MyComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 返回 false 可以阻止组件更新
    return nextProps.value !== this.props.value;
  }
  render() {
    // 渲染逻辑
  }
}

7. 使用 useDeferredValueuseTransition

useDeferredValueuseTransition 是 React 18 中新增的 Hook,用于优化用户输入的响应性能。

import { useDeferredValue, useTransition } from 'react';
const MyComponent = ({ input }) => {
  const deferredInput = useDeferredValue(input);
  const [isPending, startTransition] = useTransition();
  const handleClick = () => {
    startTransition(() => {
      // 一些需要延迟执行的操作
    });
  };
  return (
    <div>
      <input value={input} onChange={e => setInput(e.target.value)} />
      <button onClick={handleClick}>Click me</button>
      {isPending ? <span>Loading...</span> : <span>{deferredInput}</span>}
    </div>
  );
};

8. 使用 windowing 技术

当渲染大量列表项时,可以使用 react-windowreact-virtualized 库进行窗口化渲染,仅渲染可视区域内的列表项,从而提升性能。

import { FixedSizeList as List } from 'react-window';
const MyComponent = ({ items }) => (
  <List
    height={500}
    itemCount={items.length}
    itemSize={35}
    width={300}
  >
    {({ index, style }) => (
      <div style={style}>{items[index]}</div>
    )}
  </List>
);

9. 使用 useReducer 优化状态管理

对于复杂状态逻辑,可以使用 useReducer 替代 useState,从而避免多次状态更新导致的性能问题。

import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}
const MyComponent = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
};

10. 使用生产构建

确保在生产环境中使用 react-scripts build 来构建应用,以启用各种性能优化,如代码压缩和树摇优化。

npm run build

 

*** 本文仅是学习中的记录,有错误请指正。 ***
 
© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容