MST

星途 面试题库

面试题:React shouldComponentUpdate 性能优化实践

假设你正在开发一个列表展示组件,列表项数据频繁变化。请说明如何合理利用 shouldComponentUpdate 方法来优化该组件的性能。例如,如何通过比较新旧 props 和 state 中的特定字段来决定是否更新组件?同时,阐述在函数式组件中没有 shouldComponentUpdate 时,有哪些替代方案来实现类似的性能优化。
30.2万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

类组件中利用 shouldComponentUpdate 优化性能

  1. 基本概念shouldComponentUpdate 是 React 类组件的生命周期方法,它接收新的 propsstate 作为参数,返回一个布尔值。如果返回 true,则组件会重新渲染;返回 false,则组件不会重新渲染。
  2. 比较特定字段
    • 假设列表项数据在 props 中的 listData 字段,我们可以这样实现 shouldComponentUpdate
class ListComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 只比较 listData 字段
    return this.props.listData!== nextProps.listData;
  }

  render() {
    return (
      <ul>
        {this.props.listData.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    );
  }
}
  • 如果 listData 是一个对象数组,且每个对象有一个 id 字段标识唯一性,我们可以通过比较数组的长度以及每个对象的 id 来决定是否更新:
class ListComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.listData.length!== nextProps.listData.length) {
      return true;
    }
    for (let i = 0; i < this.props.listData.length; i++) {
      if (this.props.listData[i].id!== nextProps.listData[i].id) {
        return true;
      }
    }
    return false;
  }

  render() {
    return (
      <ul>
        {this.props.listData.map((item, index) => (
          <li key={index}>{item.name}</li>
        ))}
      </ul>
    );
  }
}

函数式组件的替代方案

  1. React.memo
    • React.memo 是一个高阶组件,它对函数式组件进行浅比较优化。
    • 例如,对于展示列表的函数式组件:
const ListComponent = React.memo(({ listData }) => {
  return (
    <ul>
      {listData.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
});
  • 这里 React.memo 会对 props 进行浅比较,如果 props 中的 listData 引用没有变化,组件不会重新渲染。如果 listData 是对象数组且需要深度比较,可以传递一个自定义比较函数作为第二个参数:
const areEqual = (prevProps, nextProps) => {
  if (prevProps.listData.length!== nextProps.listData.length) {
    return false;
  }
  for (let i = 0; i < prevProps.listData.length; i++) {
    if (prevProps.listData[i].id!== nextProps.listData[i].id) {
      return false;
    }
  }
  return true;
};

const ListComponent = React.memo(({ listData }) => {
  return (
    <ul>
      {listData.map((item, index) => (
        <li key={index}>{item.name}</li>
      ))}
    </ul>
  );
}, areEqual);
  1. useMemo 和 useCallback
    • useMemo 用于缓存计算结果,避免不必要的重新计算。例如,如果列表组件有一些基于 props 计算的派生数据:
const ListComponent = ({ listData }) => {
  const derivedData = useMemo(() => {
    // 复杂计算逻辑,依赖 listData
    return listData.filter(item => item.isActive);
  }, [listData]);

  return (
    <ul>
      {derivedData.map((item, index) => (
        <li key={index}>{item.name}</li>
      ))}
    </ul>
  );
};
  • useCallback 用于缓存函数定义,避免函数在每次渲染时重新创建。如果列表组件传递一个回调函数给子组件,且该回调函数依赖 propsstate
const ListComponent = ({ listData, onItemClick }) => {
  const handleClick = useCallback((item) => {
    onItemClick(item);
  }, [onItemClick]);

  return (
    <ul>
      {listData.map((item, index) => (
        <li key={index} onClick={() => handleClick(item)}>{item.name}</li>
      ))}
    </ul>
  );
};