星火艺坊首页 Swiper 性能优化实战
in 星火艺坊 with 0 comment

星火艺坊首页 Swiper 性能优化实战

in 星火艺坊 with 0 comment

优化思路和解决方案

Swiper 挂载策略(只渲染必要内容)

问题:之前“桌面端叠放所有幻灯片”,浏览器认为都在视窗内,图片/视频并发加载,首图带宽被稀释,拖慢 LCP。

核心优化思路:

前后代码对比(简化版):

未优化(所有 slide 都在 DOM 且可能请求资源)

// 反例:每一张都渲染,容易触发并发请求与解码
{slides.map((s, i) => (
  <div key={s.id} className="absolute inset-0">
    {renderMedia(s, i)}  // 每一张都会加载/解码
  </div>
))}

优化后(仅当前 + 过渡上一张;访问过的也保留挂载但不重复请求)

// 关键状态
const [current, setCurrent] = useState(0);
const [prev, setPrev] = useState<number | null>(null);
const [isTransitioning, setIsTransitioning] = useState(false);
const [renderedSlides, setRenderedSlides] = useState<Set<number>>(new Set([0]));

// 映射渲染
{slides.map((s, i) => {
  const isVisible = i === current;
  const isPrev = prev !== null && i === prev;
  const isVisited = renderedSlides.has(i);
  const shouldMount = isVisible || isPrev || isVisited;

  return (
    <div key={s.id} className={`absolute inset-0 ${isVisible ? "opacity-100" : "opacity-0"}`}>
      {shouldMount ? renderMedia(s, i) : null}
    </div>
  );
})}

说明:

媒体加载与播放策略(按需、可控)

图片

视频

代码示例:

// 仅当前视频 autoplay,其他均 preload="none"
<video
  ref={(el) => { videoRefs.current[index] = el; }}
  autoPlay={index === current}
  muted
  loop
  playsInline
  preload={index === current ? "metadata" : "none"}
  className="h-full w-full object-cover"
>
  <source src={slide.src} type="video/mp4" />
</video>

// 切换时控制播放/暂停,防止隐藏时继续拉流
useEffect(() => {
  videoRefs.current.forEach((video, i) => {
    if (!video) return;
    if (i === current) {
      video.play().catch(() => {});
    } else {
      video.pause();
    }
  });
}, [current]);

空闲时间预热下一张(避免切换瞬时卡顿)

部分代码示例:

const preheated = useRef<Set<number>>(new Set([0]));

useEffect(() => {
  if (typeof window === "undefined") return;

  const cn = (navigator as any).connection;
  if (cn?.saveData) return; // 省流模式直接跳过

  const next = (current + 1) % slides.length;
  if (renderedSlides.has(next) || preheated.current.has(next)) return;

  const run = () => {
    const s = slides[next];
    try {
      if (s.mediaType === "image") {
        const img = new Image();
        img.src = s.src;
      } else if (s.mediaType === "video") {
        const isWifiOrEth = cn?.type === "wifi" || cn?.type === "ethernet";
        const fastCell = cn?.effectiveType === "4g" || (typeof cn?.downlink === "number" && cn.downlink >= 10);
        if (!cn || isWifiOrEth || fastCell) {
          const v = document.createElement("video");
          v.preload = "metadata";
          v.src = s.src;
          try { v.load(); } catch {}
        }
      }
      preheated.current.add(next);
    } catch {}
  };

  const w = window as any;
  const cancel = typeof w.requestIdleCallback === "function"
    ? (() => { const id = w.requestIdleCallback(run, { timeout: 2000 }); return () => w.cancelIdleCallback?.(id); })()
    : (() => { const id = setTimeout(run, 800); return () => clearTimeout(id); })();

  return cancel;
}, [current, renderedSlides]);

决策图示例:

image-20250813144311632

优化影响评估

SEO 影响评估

性能收益与体验变化

注意事项与可拓展

留言: