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

React/Next.js中不使用<Suspense>或onLoad的加载屏最优实现方案

最优加载屏实现方案

针对你的需求,以下是覆盖首次访问和页面切换场景、精准控制加载时机的实现方案,解决你之前遇到的所有问题:

核心思路

  1. 区分「DOM就绪」和「全部资源加载完成」:监听全局事件确保DOM、图片、样式、脚本都加载完毕
  2. 路由联动:页面切换时重置加载状态,结合当前页面的业务逻辑(数据请求、组件渲染)控制加载屏关闭时机
  3. 平滑过渡:用CSS实现加载屏的淡出动画,避免生硬跳转

具体实现

1. 全局加载器组件(App.jsx)

结合React Router监听路由变化,同时监听全局资源加载状态:

import { useState, useEffect } from 'react';
import { useLocation, Routes, Route } from 'react-router-dom';
import ScreenLoader from './ScreenLoader';
import Home from './pages/Home';
import About from './pages/About';

function App() {
  const [isLoading, setIsLoading] = useState(true);
  const location = useLocation();

  // 首次加载:监听全局资源加载完成
  useEffect(() => {
    const handleGlobalLoad = () => {
      // 延迟300ms确保页面动画初始化完成,避免加载屏突然消失
      setTimeout(() => setIsLoading(false), 300);
    };

    // 如果DOM已经就绪,直接触发
    if (document.readyState === 'complete') {
      handleGlobalLoad();
    } else {
      window.addEventListener('load', handleGlobalLoad);
      return () => window.removeEventListener('load', handleGlobalLoad);
    }
  }, []);

  // 路由切换:重置加载状态,等待当前页面加载完成
  useEffect(() => {
    setIsLoading(true);

    // 监听页面自定义加载完成事件(由页面组件触发)
    const handlePageLoaded = () => setIsLoading(false);
    window.addEventListener('pageLoaded', handlePageLoaded);

    return () => {
      window.removeEventListener('pageLoaded', handlePageLoaded);
    };
  }, [location.pathname]);

  return (
    <>
      <ScreenLoader isVisible={isLoading} />
      <div className="app-container">
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </div>
    </>
  );
}

export default App;

2. 加载屏样式(ScreenLoader.css)

实现平滑淡出过渡,避免遮挡页面交互:

.screen-loader {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  /* 平滑过渡效果 */
  transition: opacity 0.4s ease-in-out, visibility 0.4s ease-in-out;
}

.screen-loader.hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none; /* 加载屏隐藏后不再拦截点击 */
}

/* 加载动画示例(可替换成你的动画) */
.loader-spinner {
  width: 48px;
  height: 48px;
  border: 5px solid #f3f3f3;
  border-top-color: #3498db;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

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

3. 页面组件内控制加载状态(以Home.jsx为例)

当页面数据请求、内部资源加载完成后,通知全局加载器关闭:

import { useState, useEffect } from 'react';

function Home() {
  const [pageData, setPageData] = useState(null);

  useEffect(() => {
    const loadPageResources = async () => {
      try {
        // 1. 加载页面数据
        const res = await fetch('/api/home-data');
        const data = await res.json();
        setPageData(data);

        // 2. 等待页面内所有图片加载完成
        const pageImages = document.querySelectorAll('.home-page img');
        if (pageImages.length > 0) {
          await Promise.all(
            Array.from(pageImages).map(img => {
              if (img.complete) return Promise.resolve();
              return new Promise(resolve => img.onload = resolve);
            })
          );
        }

        // 3. 通知全局加载器关闭
        window.dispatchEvent(new CustomEvent('pageLoaded'));
      } catch (err) {
        console.error('页面加载失败:', err);
        // 即使出错也关闭加载屏,避免卡住用户
        window.dispatchEvent(new CustomEvent('pageLoaded'));
      }
    };

    loadPageResources();
  }, []);

  if (!pageData) return null; // 或页面内局部加载动画

  return (
    <div className="home-page">
      {/* 页面内容 */}
      <img src="/hero-banner.jpg" alt="Hero Banner" />
      <h1>{pageData.title}</h1>
    </div>
  );
}

export default Home;

关键优化点

  • 摒弃setTimeout硬编码延迟:通过window.load事件监听全局资源,结合页面自定义事件精准控制关闭时机
  • 修复onLoad无效问题:div元素没有onLoad事件,改用全局window.load或针对具体资源(如图片)监听加载
  • 路由切换适配:通过useLocation监听路由变化,每次切换时重置加载状态,避免页面切换卡顿
  • 平滑过渡:用CSStransition实现加载屏淡出,提升用户体验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 15:47:06