图片是页面最大的性能杀手

一张未经优化的封面图可能占用 2MB,而一篇 5000 字的文章只需要 15KB。图片的加载速度决定了页面的首屏时间。

幻梦用三种策略处理图片:懒加载、灯箱、响应式。

懒加载

浏览器原生的 loading="lazy" 已经很好,但幻梦额外使用 IntersectionObserver 做更精细的控制:

class InteractionManager {
  private observer: IntersectionObserver;

  initLazyLoading(): void {
    this.observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const img = entry.target as HTMLImageElement;
          if (img.dataset.src) {
            img.src = img.dataset.src;     // 替换真实 URL
            img.removeAttribute('data-src');
          }
          this.observer.unobserve(img);
        }
      });
    }, {
      rootMargin: '200px'   // 提前 200px 开始加载
    });

    document.querySelectorAll('img[data-src]').forEach(img => {
      this.observer.observe(img);
    });
  }
}

rootMargin: '200px' 让图片在进入视口前 200px 就开始加载,用户滚动时不会看到空白。

配合一个轻量的 SVG 占位符,在图片加载前保持布局稳定,避免页面跳动。

图片灯箱

点击文章中的图片时,弹出全屏灯箱:

class UtilsManager {
  openLightbox(img: HTMLImageElement): void {
    const lightbox = document.createElement('div');
    lightbox.className = 'lightbox-overlay';

    const cloned = document.createElement('img');
    cloned.src = img.src;
    cloned.alt = img.alt;

    lightbox.appendChild(cloned);
    document.body.appendChild(lightbox);

    // 点击外部关闭
    lightbox.addEventListener('click', () => lightbox.remove());
    // Esc 关闭
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape') lightbox.remove();
    }, { once: true });
  }
}

点击背景或按 Esc 关闭,过渡动画使用 CSS transition,200ms 淡入。

响应式图片

虽然 Hugo 本身对响应式图片的支持有限,但幻梦的 CSS 保证了图片在各种屏幕上不会溢出:

.article-text img {
  max-width: 100%;
  height: auto;
  border-radius: var(--br1);
}

图片加载的每个细节都影响用户体验。懒加载、灯箱、占位符——这些细节累积起来,就是专业和业余的差距。