日历组件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
相关产品推荐
相关产品推荐

