这是一个来自谷歌Adsense应用页面的例子。加载界面显示在主界面之前。

我不知道如何用React做同样的事情,因为如果我用React组件渲染加载屏幕,它不会在页面加载时显示,因为它必须等待DOM渲染之前。

更新:

我通过将屏幕加载器放在index.html中并在React componentDidMount()生命周期方法中删除它来举例说明我的方法。

示例和反应加载屏幕。


当前回答

这将发生在ReactDOM.render()控制根目录<div>之前。也就是说,你的应用程序到那时还没有被安装。

所以你可以把你的加载器添加到index.html文件的根目录<div>。在React接管之前,这将在屏幕上可见。

您可以使用任何最适合您的加载器元素(例如带有动画的svg)。

您不需要在任何生命周期方法上删除它。React会用你渲染的<App/>替换它的根<div>的所有子元素,就像我们在下面的动图中看到的那样。

关于CodeSandbox的例子

index . html

<head>
  <style>
    .svgLoader {
      animation: spin 0.5s linear infinite;
      margin: auto;
    }
    .divLoader {
      width: 100vw;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }
  </style>
</head>

<body>
  <div id="root">
    <div class="divLoader">
      <svg class="svgLoader" viewBox="0 0 1024 1024" width="10em" height="10em">
        <path fill="lightblue"
          d="PATH FOR THE LOADER ICON"
        />
      </svg>
    </div>
  </div>
</body>

index.js

在ReactDOM.render()运行之前,使用调试器检查页面。

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

debugger; // TO INSPECT THE PAGE BEFORE 1ST RENDER

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

其他回答

我还使用了@Ori Drori的答案,并设法让它起作用。随着React代码的增长,客户端浏览器在第一次访问时必须下载的编译包也会增加。如果你处理不好,这会带来用户体验问题。

我添加到@Ori回答是添加和执行onload函数在index.html的onload属性的主体标签,以便加载器消失后,一切都已完全加载在浏览,见下面的代码片段:

<html>
  <head>
     <style>
       .loader:empty {
          position: absolute;
          top: calc(50% - 4em);
          left: calc(50% - 4em);
          width: 6em;
          height: 6em;
          border: 1.1em solid rgba(0, 0, 0, 0.2);
          border-left: 1.1em solid #000000;
          border-radius: 50%;
          animation: load8 1.1s infinite linear;
        }
        @keyframes load8 {
          0% {
           transform: rotate(0deg);
          }
          100% {
           transform: rotate(360deg);
          }
        }
     </style>
     <script>
       function onLoad() {
         var loader = document.getElementById("cpay_loader");loader.className = "";}
     </script>
   </head>
   <body onload="onLoad();">
     more html here.....
   </body>
</html>

如果有人正在为上述用例寻找一个插入式、零配置和零依赖的库,请尝试pace.js (https://codebyzach.github.io/pace/docs/)。

它自动挂钩事件(ajax, readyState,历史推送状态,js事件循环等),并显示一个可定制的加载器。

与我们的react/relay项目合作良好(使用react-router, relay请求处理导航更改) (不是affliated;在我们的项目中使用了pace.js,它工作得很好)

我最近不得不处理这个问题,并想出了一个解决方案,这对我来说很好。然而,我已经尝试了上面的@Ori Drori解决方案,不幸的是,它并没有正确工作(有一些延迟+我不喜欢setTimeout函数的使用)。

这是我想到的:

index . html文件

内头标签-指示器的样式:

<style media="screen" type="text/css">

.loading {
  -webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
  animation: sk-scaleout 1.0s infinite ease-in-out;
  background-color: black;
  border-radius: 100%;
  height: 6em;
  width: 6em;
}

.container {
  align-items: center;
  background-color: white;
  display: flex;
  height: 100vh;
  justify-content: center;
  width: 100vw;
}

@keyframes sk-scaleout {
  0% {
    -webkit-transform: scale(0);
    transform: scale(0);
  }
  100% {
    -webkit-transform: scale(1.0);
    opacity: 0;
    transform: scale(1.0);
  }
}

</style>

现在是body标签:

<div id="spinner" class="container">
  <div class="loading"></div>
</div>

<div id="app"></div>

然后是一个非常简单的逻辑,在app.js文件中(在渲染函数中):

const spinner = document.getElementById('spinner');

if (spinner && !spinner.hasAttribute('hidden')) {
  spinner.setAttribute('hidden', 'true');
}

它是如何工作的?

当第一个组件(在我的应用程序中,大多数情况下是app.js)正确安装时,旋转器将被隐藏,并应用隐藏属性给它。

更重要的是补充什么- spinner. hasattribute ('hidden')条件阻止在每次组件挂载时向spinner添加隐藏属性,因此实际上它只会在整个应用程序加载时添加一次。

如果你使用react-router来管理你的应用程序的路由,你可以很容易地添加加载屏幕与我做的react-router加载库。

它也会影响页面切换,但我认为,如果你想预装第一页,自然也会预装其他页面。

这个方法和悬疑方法的区别在于,有了这个库,你可以在获取数据的同时继续加载。 基本上,这个方法非常类似于在组件中使用isLoading状态,但是如果您有很多不同的页面,则更容易实现。

使用

在路由器部分,从react-router-loading导入Switch和Route,而不是从react-router-dom导入

import { Switch, Route } from "react-router-loading";

<Switch>
    <Route path="/page1" component={Page1} />
    <Route path="/page2" component={Page2} />
    ...
</Switch>

在切换前必须加载的每条路线上添加加载道具

<Switch>
    // data will be loaded before switching
    <Route path="/page1" component={Page1} loading />

    // instant switch as before
    <Route path="/page2" component={Page2} />
    ...
</Switch>

在加载道具路由中提到的组件的初始加载方法的末尾添加loadingContext.done()(在这种情况下是Page1)

import { LoadingContext } from "react-router-loading";
const loadingContext = useContext(LoadingContext);

const loading = async () => {
    // loading some data

    // call method to indicate that loading is done and we are ready to switch
    loadingContext.done();
};

你可以指定加载屏幕,将显示在你的应用程序的第一次加载

const MyLoadingScreen = () => <div>Loading...</div>

<Switch loadingScreen={MyLoadingScreen}>
...
</Switch>

我不知道现在回答是否太晚,因为你可能已经找到了解决方案,但这里有一个来自我的观点,因为这个问题真的很有用。: 我在scrimba.com上上了一堂课,在这里,老师从课堂开始,然后开始讲课。他通过课堂和状态来教授API调用。这是他的代码:

import React, {Component} from "react"

class App extends Component {
    constructor() {
        super()
        this.state = {
            loading: false,
            character: {}
        }
    }
    
    componentDidMount() {
        this.setState({loading: true})
        fetch("https://swapi.dev/api/people/1/")
            .then(response => response.json())
            .then(data => {
                this.setState({
                    loading: false,
                    character: data
                })
            })
    }
    
    render() {
        const text = this.state.loading ? "loading..." : this.state.character.name
        return (
            <div>
                <p>{text}</p>
            </div>
        )
    }
}

export default App

这很直接,在开始时将加载状态设置为true并保持它,直到接收到数据,然后当接收到数据时,更改状态并将加载设置为false并显示内容。 现在我试着用钩子,作为练习,它工作得很顺利!一个简单而有效的解决方案。这是我的代码:

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

function App()
{
    const [response, setResponse] = useState([]);
    
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        fetchResponse() ;
    } , []);

    const fetchResponse = async () => {
        const data = await fetch("https://swapi.dev/api/people/1/");
        const response = await data.json();

        setResponse(response);
        console.log(response.name);
        setLoading(false);
    } 

        const content = loading ? <i className="fas fa-atom fa-spin"></i> : <h1>{response.name}</h1>

    return(
        <section id="w-d-p">
            {content}
        </section>
    )
}

export default App;

钩子也是一样的逻辑。在数据加载时,我得到了漂亮的转轮然后,我的数据!

哦,顺便说一下,如果你不喜欢这个XD,你可以在fetch中放入自己的API。