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

React:如何避免组件逻辑切换时的双渲染卡顿问题

问题描述

我通过state控制图片未加载时渲染占位组件,但图片加载完成瞬间,图片与占位组件会同时渲染,导致容器高度短暂翻倍后才移除占位组件。

若将图片改为条件渲染{loaded && <img onLoad={() => setLoaded(true)} src={props.imageURL} alt=""/>},则仅占位组件渲染,图片因无法触发onLoad始终无法加载。

另外需注意:占位组件并非图片,无法使用fallback源。请问有什么优化方案可以避免这种双渲染/卡顿问题?

我的代码如下:

export const StyledCard = (props: CardProps) => {
    const [loaded, setLoaded] = React.useState<boolean>(false);
    
    return (
        <StyledContainer>
            <StyledImage onLoad={() => setLoaded(true)} src={props.imageURL} alt=""/>
            {!loaded && <ComponentWhichIsNotAnImageAndCannotBeUsedAsFallbackSource />}
        </StyledContainer>
    )
}
优化方案

方案1:初始隐藏图片,加载完成后显示

给图片设置初始隐藏样式,保证它能触发onLoad但不占据布局空间,加载完成后再显示并移除占位组件,避免高度跳动。

修改后的代码:

export const StyledCard = (props: CardProps) => {
    const [loaded, setLoaded] = React.useState<boolean>(false);
    
    return (
        <StyledContainer>
            <StyledImage 
                style={{ 
                    opacity: loaded ? 1 : 0,
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    width: '100%',
                    height: '100%'
                }}
                onLoad={() => setLoaded(true)} 
                src={props.imageURL} 
                alt=""
            />
            {!loaded && <ComponentWhichIsNotAnImageAndCannotBeUsedAsFallbackSource />}
        </StyledContainer>
    )
}

补充:如果容器需要自适应高度,可以给占位组件设置和图片最终一致的高度,或者提前通过接口获取图片尺寸并设置容器高度。

方案2:预加载图片后再渲染

在组件挂载时创建Image对象预加载图片,加载完成后再将图片渲染到DOM中,这样占位组件会直接被替换,不会出现两者同时存在的情况。

修改后的代码:

export const StyledCard = (props: CardProps) => {
    const [loaded, setLoaded] = React.useState<boolean>(false);
    
    React.useEffect(() => {
        const img = new Image();
        img.src = props.imageURL;
        img.onload = () => setLoaded(true);
        // 可选:处理图片加载失败的情况
        img.onerror = () => {
            console.error('图片加载失败');
        };
    }, [props.imageURL]);
    
    return (
        <StyledContainer>
            {loaded && <StyledImage src={props.imageURL} alt="" />}
            {!loaded && <ComponentWhichIsNotAnImageAndCannotBeUsedAsFallbackSource />}
        </StyledContainer>
    )
}

方案3:用绝对定位让两者重叠

给容器设置相对定位,让图片和占位组件绝对定位在同一位置,这样即使两者短暂共存,也不会改变容器高度,视觉上无卡顿。

修改后的代码(含样式):

import styled from 'styled-components';

const StyledContainer = styled.div`
    position: relative;
    width: 100%;
    min-height: 200px;
`;

const StyledImage = styled.img`
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
`;

export const StyledCard = (props: CardProps) => {
    const [loaded, setLoaded] = React.useState<boolean>(false);
    
    return (
        <StyledContainer>
            <StyledImage onLoad={() => setLoaded(true)} src={props.imageURL} alt="" />
            {!loaded && (
                <ComponentWhichIsNotAnImageAndCannotBeUsedAsFallbackSource 
                    style={{
                        position: 'absolute',
                        top: 0,
                        left: 0,
                        width: '100%',
                        height: '100%'
                    }} 
                />
            )}
        </StyledContainer>
    )
}

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 12:30:56