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

React客户端渲染:如何预加载资源并显示全局加载页?

在React路由外层实现全局资源预加载与加载页

核心思路

在路由最外层封装一个全局加载组件,统一处理自定义字体预加载和页面所需图片预加载,通过状态控制加载页与路由内容的切换,无需在每个组件内重复编写加载逻辑。

实现步骤与示例代码

1. 全局预加载组件 ResourceLoader

该组件负责加载指定资源,监听所有资源的加载状态,完成后渲染路由内容:

import { useState, useEffect } from 'react';

// 定义需要预加载的核心资源列表
const PRELOAD_RESOURCES = {
  // 自定义字体:键为字体名称,值为字体文件路径
  fonts: [
    { name: 'YourCustomFont', src: '/fonts/your-custom-font.woff2' },
    { name: 'SecondaryFont', src: '/fonts/secondary-font.woff2' }
  ],
  // 首屏及核心页面需要的图片
  images: [
    '/images/hero-banner.jpg',
    '/images/site-logo.png'
  ]
};

const ResourceLoader = ({ children }) => {
  const [allLoaded, setAllLoaded] = useState(false);
  const [hasError, setHasError] = useState(false);

  useEffect(() => {
    let isComponentMounted = true;
    const loadTasks = [];

    // 加载自定义字体
    PRELOAD_RESOURCES.fonts.forEach(font => {
      const fontFace = new FontFace(font.name, `url(${font.src})`);
      const fontLoadTask = fontFace.load()
        .then(loadedFont => document.fonts.add(loadedFont))
        .catch(err => {
          console.error(`字体 ${font.name} 加载失败:`, err);
          setHasError(true);
        });
      loadTasks.push(fontLoadTask);
    });

    // 预加载图片
    PRELOAD_RESOURCES.images.forEach(imgUrl => {
      const imgLoadTask = new Promise((resolve, reject) => {
        const img = new Image();
        img.src = imgUrl;
        img.onload = resolve;
        img.onerror = () => {
          console.error(`图片 ${imgUrl} 加载失败`);
          reject();
        };
      });
      loadTasks.push(imgLoadTask);
    });

    // 等待所有资源加载完成(允许部分失败,根据业务调整)
    Promise.allSettled(loadTasks)
      .then(results => {
        if (!isComponentMounted) return;
        const failedTasks = results.filter(res => res.status === 'rejected');
        if (failedTasks.length) setHasError(true);
        setAllLoaded(true);
      });

    // 组件卸载时取消状态更新
    return () => {
      isComponentMounted = false;
    };
  }, []);

  // 加载中显示加载页
  if (!allLoaded && !hasError) {
    return (
      <div style={{ 
        display: 'flex', 
        justifyContent: 'center', 
        alignItems: 'center', 
        height: '100vh',
        backgroundColor: '#f8f9fa'
      }}>
        <div style={{ fontSize: '1.2rem', color: '#6c757d' }}>页面加载中...</div>
        {/* 可替换为自定义加载动画 */}
      </div>
    );
  }

  // 资源加载失败时显示降级提示
  if (hasError) {
    return (
      <div style={{ 
        display: 'flex', 
        justifyContent: 'center', 
        alignItems: 'center', 
        height: '100vh',
        color: '#dc3545'
      }}>
        <div>部分资源加载失败,请刷新页面重试</div>
      </div>
    );
  }

  // 所有资源加载完成,渲染路由内容
  return children;
};

export default ResourceLoader;

2. 在路由外层包裹该组件

以react-router-dom为例,在路由配置的最外层嵌套ResourceLoader:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import ResourceLoader from './ResourceLoader';
import Home from './pages/Home';
import About from './pages/About';

function App() {
  return (
    <Router>
      <ResourceLoader>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          {/* 其他路由配置 */}
        </Routes>
      </ResourceLoader>
    </Router>
  );
}

export default App;

额外说明

  • 动态资源处理:如果部分页面的图片/字体是接口返回的动态资源,可以在对应页面的useEffect中补充预加载逻辑,或在ResourceLoader中根据当前路由动态更新资源列表。
  • 字体加载兜底:若希望在字体加载失败时使用系统字体,可在CSS中为元素设置font-family: YourCustomFont, sans-serif;作为降级方案。
  • 性能优化:仅预加载首屏和核心资源,非核心资源可使用懒加载(如IntersectionObserver)避免延长初始加载时间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 03:01:20