You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

日历组件map渲染时重复执行函数导致性能卡顿的优化问询

日历组件切换月份时的性能优化方案

问题场景

开发了一个展示当月所有日期的日历组件,点击左右箭头可切换上下月,但切换几次后页面出现卡顿。核心问题是每次切换月份时,都会循环渲染当月每一天,且每个日期都重复执行toLocaleDateString日期格式化和节假日(Feiertag)检查操作,31天的月份每次都要重复计算这些内容,导致性能浪费。

原代码(中文注释版)

// 判断给定日期是否为节假日
const checkIfFeiertag = (dateToFilter) => {
  return feiertage.filter(
    (isFeiertag) => isFeiertag.dateString === dateToFilter
  );
};

{Array(days)
  .fill()
  .map((_, day) => {
    var newDate = new Date(
      date.getFullYear(),
      date.getMonth(),
      day + 1
    );

    const result = newDate.toLocaleDateString("sv-SE", {
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
    });

    const checkResult = checkIfFeiertag(result);

    return (
      <div className="flex flex-col group p-1 " key={day}>
        ...
      </div>
    )
  }
}

优化方案

1. 用哈希表替换数组遍历,降低节假日检查的时间复杂度

当前checkIfFeiertag使用filter遍历整个节假日数组,每次检查的时间复杂度是O(n)。换成Map存储节假日数据,可将查找时间降到O(1),大幅减少重复遍历的开销。

// 用useMemo缓存节假日哈希表,仅当feiertage变化时重新生成
const feiertagMap = useMemo(() => {
  const map = new Map();
  feiertage.forEach(item => {
    map.set(item.dateString, item);
  });
  return map;
}, [feiertage]);

// 优化后的节假日检查函数
const checkIfFeiertag = (dateToFilter) => {
  return feiertagMap.get(dateToFilter) ? [feiertagMap.get(dateToFilter)] : [];
};

2. 预计算当月所有日期的核心数据,避免循环内重复计算

利用useMemo一次性计算好当月所有日期的格式化字符串、节假日状态等数据,仅在月份(date)、当月天数(days)或节假日数据(feiertagMap)变化时重新计算,渲染时直接使用预计算结果。

// 预计算当月所有日期的核心数据
const monthDates = useMemo(() => {
  return Array(days).fill().map((_, day) => {
    const newDate = new Date(
      date.getFullYear(),
      date.getMonth(),
      day + 1
    );
    const dateString = newDate.toLocaleDateString("sv-SE", {
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
    });
    const feiertagInfo = feiertagMap.get(dateString) || null;
    return {
      date: newDate,
      dateString,
      feiertagInfo
    };
  });
}, [date, days, feiertagMap]);

// 渲染时直接使用预计算好的数据
{monthDates.map((dateItem, index) => {
  return (
    <div className="flex flex-col group p-1 " key={index}>
      {/* 直接使用dateItem.dateString、dateItem.feiertagInfo等 */}
      ...
    </div>
  )
})}

3. 可选:手动生成日期字符串,减少Date实例开销

如果你的日历固定使用"sv-SE"格式(YYYY-MM-DD),可以手动拼接日期字符串,避免重复创建Date实例和调用toLocaleDateString:

// 在预计算函数中替换toLocaleDateString
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需+1并补0
const dateString = `${year}-${month}-${String(day + 1).padStart(2, '0')}`;

4. 组件拆分与memo化,避免不必要的重渲染

如果每个日期项是独立的子组件,用React.memo包裹子组件,确保只有当日期数据变化时才重新渲染:

// 用React.memo包裹日期子组件
const DateItem = React.memo(({ dateData }) => {
  return (
    <div className="flex flex-col group p-1">
      {/* 渲染日期内容 */}
    </div>
  );
});

// 渲染时使用子组件
{monthDates.map((dateItem, index) => (
  <DateItem key={index} dateData={dateItem} />
))}

内容的提问来源于stack exchange,提问作者Captai-N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 04:05:58