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

如何在Gatsby站点加载大体积page-data.json时显示加载器?

嘿,这个问题我在Gatsby项目里踩过坑!你遇到的核心问题是Gatsby的页面渲染逻辑:首次加载时,HTML骨架会先返回,但page-data.json(就是你说的10MB数据文件)是异步加载的,这时候组件里的数组可能已经被初始化(比如空数组或者构建时的默认值),导致array.length判断失效。下面给你几个靠谱的解决方案:

1. 最稳妥:修改Gatsby的HTML模板(处理首次加载)

这个方法能直接覆盖首次加载时page-data.json下载的空档,因为HTML是最先渲染的,加载动画会立刻显示,直到Gatsby完成初始化。

首先在src/html.js里(如果没有就从Gatsby默认模板复制一份),在Gatsby的根容器前加加载动画:

<body>
  <!-- 加载动画容器 -->
  <div id="initial-loading">
    <div class="spinner"></div>
  </div>
  <div id="___gatsby"></div>
  
  <script>
    // 监听页面完全加载(包括page-data),然后隐藏动画
    window.addEventListener('load', () => {
      // 加个小延迟避免闪烁
      setTimeout(() => {
        document.getElementById('initial-loading').style.display = 'none';
      }, 200);
    });
  </script>
</body>

然后加对应的CSS(可以放在全局样式文件里):

#initial-loading {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: #fff; /* 匹配你的页面背景色 */
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}

.spinner {
  width: 50px;
  height: 50px;
  border: 4px solid #e0e0e0;
  border-top-color: #1976d2; /* 用你的主题色 */
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

2. 组件级控制:如果数据是客户端动态加载

如果你的视频卡数据不是预渲染在page-data里,而是组件mount后才fetch的,那直接用组件内部状态就能解决:

import React, { useState, useEffect } from 'react';
import { FixedSizeList as List } from 'react-window';

const VideoCardList = () => {
  const [videos, setVideos] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const loadVideos = async () => {
      // 替换成你的数据请求逻辑
      const res = await fetch('/api/videos');
      const videoData = await res.json();
      
      setVideos(videoData);
      setIsLoading(false);
    };

    loadVideos();
  }, []);

  // 加载中显示spinner
  if (isLoading) {
    return (
      <div style={{ display: 'flex', justifyContent: 'center', padding: '2rem' }}>
        <div className="spinner"></div>
      </div>
    );
  }

  // 渲染react-window列表
  return (
    <List
      height={600}
      itemCount={videos.length}
      itemSize={120}
      width="100%"
    >
      {({ index, style }) => (
        <div style={style} className="video-card">
          {/* 你的视频卡内容 */}
          {videos[index].title}
        </div>
      )}
    </List>
  );
};

export default VideoCardList;

3. 进阶:监听page-data.json的加载事件

如果你非要直接检测page-data.json的加载状态,可以用一个自定义Hook来监听fetch请求:

import React, { useState, useEffect } from 'react';

const usePageDataLoaded = () => {
  const [isLoaded, setIsLoaded] = useState(false);

  useEffect(() => {
    const handleFetch = (event) => {
      const requestUrl = event.request.url;
      // 判断是否是page-data请求
      if (requestUrl.includes('/page-data/') && requestUrl.endsWith('page-data.json')) {
        // 克隆请求并跟踪完成状态
        event.request.clone().then(() => setIsLoaded(true))
          .catch(() => setIsLoaded(true)); // 出错也标记为完成
      }
    };

    window.addEventListener('fetch', handleFetch);

    // 初始化时检查是否已经加载完成
    if (window.__GATSBY__?.pageData) {
      setIsLoaded(true);
    }

    return () => window.removeEventListener('fetch', handleFetch);
  }, []);

  return isLoaded;
};

// 在主页组件中使用
const HomePage = () => {
  const pageDataLoaded = usePageDataLoaded();

  if (!pageDataLoaded) {
    return <div id="initial-loading"><div className="spinner"></div></div>;
  }

  return <VideoCardList />;
};

这个方法有点依赖Gatsby的内部实现,但当前版本(v4/v5)是有效的,适合需要精确控制的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:56:03