如何避免在多处复用的React组件中重复渲染图片?
解决图片复用加载闪烁问题
核心原因
图片虽在首页已加载并被浏览器缓存,但组件复用时仍会出现闪烁:
- 组件卸载后重新挂载,
<img>元素被重新创建,浏览器需重新验证缓存,产生短暂延迟 - 初始设为
display:none的图片未提前加载,切换显示时触发加载流程
解决方案
1. 全局预加载图片
在应用初始化阶段(如入口文件或首页组件)预加载所有目标图片,强制浏览器缓存资源:
// 在App.js或首页组件中添加 useEffect(() => { const imageUrls = [maleImageUrl, femaleImageUrl, dogImageUrl]; imageUrls.forEach(url => { const img = new Image(); img.src = url; }); }, []);
2. 保持图片DOM元素持久化
避免用display:none隐藏图片,改用visibility/opacity配合绝对定位,让图片始终存在于DOM中:
// 定义图片显示/隐藏样式逻辑 const getImageStyle = (isVisible) => ({ visibility: isVisible ? 'visible' : 'hidden', opacity: isVisible ? 1 : 0, position: 'absolute', top: 0, left: 0, transition: 'opacity 0.3s ease' // 可添加过渡动画优化体验 }); // 修改组件返回结构 return ( <Box style={{ position: 'relative', width: '100%', height: '82vh' }}> <img src={male} className={classes.image} style={getImageStyle(showMale)} /> <img src={female} className={classes.image} style={getImageStyle(showFemale)} /> <img src={dog} className={classes.image} style={getImageStyle(currentUser?.userType === 'trainer')} /> </Box> );
3. 优化Redux选择器稳定性
用shallowEqual确保useAppSelector返回值引用稳定,避免不必要的组件重渲染:
import { shallowEqual, useAppSelector } from '../../redux-store/hooks'; // 修改选择器调用 const currentUser = useAppSelector((state) => state.auth.currentUser, shallowEqual);
4. 修正代码错误
代码中dog的useMemo依赖了不存在的trainerImageUrl,需改为dogImageUrl:
const dog = useMemo(() => dogImageUrl, []);
完整修改后的组件代码
import React, { useEffect, useState, useMemo, memo } from 'react'; import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; import { Box } from '@material-ui/core/'; import { shallowEqual, useAppSelector } from '../../redux-store/hooks'; interface Props { homePage: boolean; loading: boolean; } const useStyles = makeStyles((theme: Theme) => createStyles({ image: { maxWidth: '100vw', marginTop: 'auto', backgroundRepeat: 'no-repeat', maxHeight: '82vh', backgroundSize: 'auto', filter: 'grayscale(50%) drop-shadow(12px 12px 12px rgba(128,128,128 ,0.1))', }, }) ); const SplashImages: React.FC<Props> = memo(({ homePage, loading }) => { const maleImageUrl = // 你的图片URL const femaleImageUrl = // 你的图片URL const dogImageUrl = // 你的图片URL const male = useMemo(() => maleImageUrl, []); const female = useMemo(() => femaleImageUrl, []); const dog = useMemo(() => dogImageUrl, []); const classes = useStyles(); const currentUser = useAppSelector((state) => state.auth.currentUser, shallowEqual); const [isMale, setIsMale] = useState(true); useEffect(() => { const timeoutId = setTimeout(() => { setIsMale(!isMale); }, 3000); return () => clearTimeout(timeoutId); }, [isMale]); const showMale = currentUser?.userType !== 'dog' && isMale; const showFemale = currentUser?.userType !== 'dog' && !isMale; const showDog = currentUser?.userType === 'trainer'; const getImageStyle = (isVisible) => ({ visibility: isVisible ? 'visible' : 'hidden', opacity: isVisible ? 1 : 0, position: 'absolute', top: 0, left: 0, transition: 'opacity 0.3s ease' }); return ( <Box style={{ position: 'relative', width: '100%', height: '82vh' }}> <img src={male} className={classes.image} style={getImageStyle(showMale)} /> <img src={female} className={classes.image} style={getImageStyle(showFemale)} /> <img src={dog} className={classes.image} style={getImageStyle(showDog)} /> </Box> ); }); export default SplashImages;
内容的提问来源于stack exchange,提问作者Richardson
相关产品推荐
相关产品推荐

