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

React中window load事件不生效,如何实现页面加载器?

问题分析与解决方案

为什么你的Loader组件里的window.load事件没触发?

window的load事件是在页面所有资源(图片、样式表、脚本等)加载完成后才会触发。而React组件的useEffect(空依赖数组)是在组件挂载到DOM之后才执行的——如果页面的资源在React完成初始化、Loader组件挂载之前就已经加载完毕,这时候再添加load事件监听器,自然不会触发。

你对React渲染机制的误解

虽然你把Loader组件放在App.jsx的最前面导入,但React的组件渲染流程是:

  1. React框架本身先初始化
  2. 开始渲染组件树,把Loader组件挂载到DOM
  3. 执行Loader组件的useEffect回调

这个流程的启动时机是在DOMContentLoaded事件之后(也就是HTML解析完成后),但window.load事件是等待所有资源加载完成,所以如果页面资源加载速度快于React初始化+组件挂载的速度,load事件会在useEffect执行前就已经触发,导致监听器无法捕获到事件。

实现页面加载器的替代方案

方案1:在index.html中提前渲染Loader,资源加载完成后再显示React内容

这是最可靠的方式,Loader会在页面一开始就显示,不需要等待React初始化:

<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <!-- 你的meta、样式等 -->
  <link rel="stylesheet" href="./loader.sass"> <!-- 提前引入Loader样式 -->
</head>
<body>
  <!-- 先渲染Loader -->
  <div id="loader">
    <div class="center">
      <div class="rect"></div>
      <div class="rect"></div>
    </div>
  </div>
  <!-- React根容器默认隐藏 -->
  <div id="root" style="display: none;"></div>

  <script>
    // 监听页面所有资源加载完成
    window.addEventListener('load', () => {
      // 隐藏Loader
      document.getElementById('loader').style.display = 'none';
      // 显示React根容器并渲染应用
      document.getElementById('root').style.display = 'block';
      ReactDOM.render(<App />, document.getElementById('root'));
    });
  </script>
</body>
</html>

这样能确保Loader在所有资源加载完成前一直显示,完全覆盖你的需求。

方案2:检查document.readyState,兼容事件已触发的情况

如果一定要在React组件里实现,可以先检查当前文档状态,避免错过已经触发的load事件:

import React, { useState, useEffect } from "react"
import './loader.sass'

const Loader = () => {
  const [isPageLoaded, setIsPageLoaded] = useState(false);

  useEffect(() => {
    const handlePageLoad = () => setIsPageLoaded(true);

    // 如果文档已经加载完成,直接触发回调
    if (document.readyState === 'complete') {
      handlePageLoad();
    } else {
      // 否则添加监听器,记得清理
      window.addEventListener('load', handlePageLoad);
      return () => window.removeEventListener('load', handlePageLoad);
    }
  }, []);

  // 加载完成后不渲染Loader
  if (isPageLoaded) return null;

  return (
    <div id="loader">
      <div className="center">
        <div className="rect"></div>
        <div className="rect"></div>
      </div>
    </div>
  )
}

export default Loader

方案3:手动监听特定资源加载(适合需要等待部分资源的场景)

如果不需要等待所有资源,只需要等待特定的图片、样式,可以用Promise手动监听:

import React, { useState, useEffect } from "react"
import './loader.sass'

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

  useEffect(() => {
    const resourcePromises = [];

    // 监听所有img标签的加载
    document.querySelectorAll('img').forEach(img => {
      if (!img.complete) {
        resourcePromises.push(
          new Promise(resolve => {
            img.onload = resolve;
            img.onerror = resolve; // 加载失败也算完成
          })
        );
      }
    });

    // 监听样式表加载
    document.querySelectorAll('link[rel="stylesheet"]').forEach(link => {
      if (!link.sheet) {
        resourcePromises.push(
          new Promise(resolve => {
            link.onload = resolve;
            link.onerror = resolve;
          })
        );
      }
    });

    // 所有资源加载完成后隐藏Loader
    Promise.all(resourcePromises).then(() => setIsLoaded(true));
  }, []);

  if (isLoaded) return null;

  return (
    <div id="loader">
      <div className="center">
        <div className="rect"></div>
        <div className="rect"></div>
      </div>
    </div>
  )
}

export default Loader

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:11:26