225

这是来自 Google Adsense 应用程序页面的示例。在主页显示之后显示的加载屏幕。

在此处输入图像描述

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

更新

我通过将屏幕加载程序放入index.html并在 ReactcomponentDidMount()生命周期方法中将其删除来举例说明我的方法。

示例react-loading-screen

4

23 回答 23

237

目标

渲染 html 页面时,立即显示一个微调器(在 React 加载时),并在 React 准备好后隐藏它。

由于微调器是用纯 HTML/CSS 渲染的(在 React 域之外),React 不应该直接控制显示/隐藏过程,并且实现对 React 应该是透明的。

解决方案 1 - :empty 伪类

由于您将 react 渲染到 DOM 容器中 - <div id="app"></div>,因此您可以向该容器添加一个微调器,当 react 加载和渲染时,微调器将消失。

您不能在 react 根中添加 DOM 元素(例如 div),因为 React 将在ReactDOM.render()调用后立即替换容器的内容。即使您渲染null,内容仍然会被评论替换 - <!-- react-empty: 1 -->。这意味着,如果您想在主组件挂载时显示加载器,数据正在加载,但实际上没有渲染,则放置在容器内的加载器标记(<div id="app"><div class="loader"></div></div>例如)将不起作用。

一种解决方法是将微调器类添加到反应容器,并使用:empty伪类。只要没有将任何内容渲染到容器中,微调器就会可见(注释不计在内)。一旦 react 渲染了评论以外的东西,加载器就会消失。

示例 1

null在示例中,您可以看到一个在准备好之前呈现的组件。容器也是加载器 - <div id="app" class="app"></div>,加载器的类只有在它是:empty(参见代码中的注释)时才会起作用:

class App extends React.Component {
  state = {
    loading: true
  };

  componentDidMount() {
    // this simulates an async action, after which the component will render the content
    demoAsyncCall().then(() => this.setState({ loading: false }));
  }
  
  render() {
    const { loading } = this.state;
    
    if(loading) { // if your component doesn't have to wait for an async action, remove this block 
      return null; // render null when app is not ready
    }
    
    return (
      <div>I'm the app</div>
    ); 
  }
}

function demoAsyncCall() {
  return new Promise((resolve) => setTimeout(() => resolve(), 2500));
}

ReactDOM.render(
  <App />,
  document.getElementById('app')
);
.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);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>

<div id="app" class="loader"></div> <!-- add class loader to container -->

示例 2

使用:empty伪类显示/隐藏选择器的一种变体是将微调器设置为应用容器的同级元素,并在容器为空时使用相邻的同级组合器( +) 显示它:

class App extends React.Component {
  state = {
    loading: true
  };

  componentDidMount() {
    // this simulates an async action, after which the component will render the content
    demoAsyncCall().then(() => this.setState({ loading: false }));
  }
  
  render() {
    const { loading } = this.state;
    
    if(loading) { // if your component doesn't have to wait for async data, remove this block 
      return null; // render null when app is not ready
    }
    
    return (
      <div>I'm the app</div>
    ); 
  }
}

function demoAsyncCall() {
  return new Promise((resolve) => setTimeout(() => resolve(), 2500));
}

ReactDOM.render(
  <App />,
  document.getElementById('app')
);
#app:not(:empty) + .sk-cube-grid {
  display: none;
}

.sk-cube-grid {
  width: 40px;
  height: 40px;
  margin: 100px auto;
}

.sk-cube-grid .sk-cube {
  width: 33%;
  height: 33%;
  background-color: #333;
  float: left;
  animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
}

.sk-cube-grid .sk-cube1 {
  animation-delay: 0.2s;
}

.sk-cube-grid .sk-cube2 {
  animation-delay: 0.3s;
}

.sk-cube-grid .sk-cube3 {
  animation-delay: 0.4s;
}

.sk-cube-grid .sk-cube4 {
  animation-delay: 0.1s;
}

.sk-cube-grid .sk-cube5 {
  animation-delay: 0.2s;
}

.sk-cube-grid .sk-cube6 {
  animation-delay: 0.3s;
}

.sk-cube-grid .sk-cube7 {
  animation-delay: 0s;
}

.sk-cube-grid .sk-cube8 {
  animation-delay: 0.1s;
}

.sk-cube-grid .sk-cube9 {
  animation-delay: 0.2s;
}

@keyframes sk-cubeGridScaleDelay {
  0%,
  70%,
  100% {
    transform: scale3D(1, 1, 1);
  }
  35% {
    transform: scale3D(0, 0, 1);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>

<div id="app"></div>
<!-- add class loader to container -->

<div class="sk-cube-grid">
  <div class="sk-cube sk-cube1"></div>
  <div class="sk-cube sk-cube2"></div>
  <div class="sk-cube sk-cube3"></div>
  <div class="sk-cube sk-cube4"></div>
  <div class="sk-cube sk-cube5"></div>
  <div class="sk-cube sk-cube6"></div>
  <div class="sk-cube sk-cube7"></div>
  <div class="sk-cube sk-cube8"></div>
  <div class="sk-cube sk-cube9"></div>
</div>


解决方案 2 - 将微调器“处理程序”作为道具传递

要对微调器显示状态进行更细粒度的控制,请创建两个函数showSpinnerhideSpinner,并通过 props 将它们传递给根容器。这些函数可以操作 DOM,或者做任何需要控制微调器的事情。这样,React 就不会感知“外界”,也不需要直接控制 DOM。您可以轻松地替换功能进行测试,或者如果您需要更改逻辑,您可以将它们传递给 React 树中的其他组件。

示例 1

const loader = document.querySelector('.loader');

// if you want to show the loader when React loads data again
const showLoader = () => loader.classList.remove('loader--hide');

const hideLoader = () => loader.classList.add('loader--hide');

class App extends React.Component {
  componentDidMount() {
    this.props.hideLoader();
  }
  
  render() {   
    return (
      <div>I'm the app</div>
    ); 
  }
}

// the setTimeout simulates the time it takes react to load, and is not part of the solution
setTimeout(() => 
  // the show/hide functions are passed as props
  ReactDOM.render(
    <App
      hideLoader={hideLoader}
      showLoader={showLoader} 
      />,
    document.getElementById('app')
  )
, 1000);
.loader {
  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;
  transition: opacity 0.3s;
}

.loader--hide {
  opacity: 0;
}

@keyframes load8 {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>

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

<div class="loader"></div>

示例 2 - 钩子

此示例使用useEffect钩子在组件安装后隐藏微调器。

const { useEffect } = React;

const loader = document.querySelector('.loader');

// if you want to show the loader when React loads data again
const showLoader = () => loader.classList.remove('loader--hide');

const hideLoader = () => loader.classList.add('loader--hide');

const App = ({ hideLoader }) => {
  useEffect(hideLoader, []);
  
  return (
    <div>I'm the app</div>
  ); 
}

// the setTimeout simulates the time it takes react to load, and is not part of the solution
setTimeout(() => 
  // the show/hide functions are passed as props
  ReactDOM.render(
    <App
      hideLoader={hideLoader}
      showLoader={showLoader} 
      />,
    document.getElementById('app')
  )
, 1000);
.loader {
  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;
  transition: opacity 0.3s;
}

.loader--hide {
  opacity: 0;
}

@keyframes load8 {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

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

<div class="loader"></div>

于 2016-12-06T06:29:32.083 回答
136

这可以通过将加载图标放置在您的 html 文件(例如 index.html )中来完成,以便用户在加载 html 文件后立即看到该图标。

当您的应用程序完成加载时,您可以简单地在生命周期挂钩中删除该加载图标,我通常在componentDidMount.

于 2016-12-06T05:51:59.050 回答
56

解决方法是:

在你的渲染功能做这样的事情:

constructor() {
    this.state = { isLoading: true }
}

componentDidMount() {
    this.setState({isLoading: false})
}

render() {
    return(
        this.state.isLoading ? *showLoadingScreen* : *yourPage()*
    )
}

在构造函数中将 isLoading 初始化为 true,在 componentDidMount 上初始化为 false

于 2016-12-06T05:46:23.177 回答
31

这将在ReactDOM.render()控制root <div>之前发生。即您的应用程序将不会安装到那个点。

因此,您可以将加载程序添加index.html到根目录中的文件中<div>。在 React 接管之前,这将在屏幕上可见。

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

您不需要在任何生命周期方法中删除它。正如我们在下面的 GIF 中看到的, React 将用您的 render替换其根目录 的任何孩子。<div><App/>

CodeSandbox 上的示例

在此处输入图像描述

索引.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

用于在运行debugger前检查页面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);
于 2019-05-08T18:19:32.050 回答
22

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

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

与我们的 react/relay 项目配合良好(使用 react-router 处理导航更改,中继请求) (不附属;在我们的项目中使用了 pace.js,效果很好)

于 2017-04-22T02:33:31.567 回答
14

当你的 React 应用程序很大时,在页面加载后启动并运行它确实需要时间。假设您将应用程序的 React 部分安装到#app. 通常,index.html 中的这个元素只是一个空的 div:

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

相反,您可以做的是在其中放置一些样式和一堆图像,以使其在页面加载和初始 React 应用程序渲染到 DOM 之间看起来更好:

<div id="app">
  <div class="logo">
    <img src="/my/cool/examplelogo.svg" />
  </div>
  <div class="preload-title">
    Hold on, it's loading!
  </div>
</div>

页面加载后,用户会立即看到 index.html 的原始内容。不久之后,当 React 准备好将渲染组件的整个层次结构安装到这个 DOM 节点时,用户将看到实际的应用程序。

注意class,不是className。这是因为您需要将其放入您的 html 文件中。


如果您使用 SSR,事情就不那么复杂了,因为用户实际上会在页面加载后立即看到真实的应用程序。

于 2016-12-06T05:55:22.937 回答
9

现在我们也可以在 React 16.8 中使用钩子:

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

const App = () => {
  const [ spinner, setSpinner ] = useState(true);

  // It will be executed before rendering

  useEffect(() => {
    setTimeout(() => setSpinner(false), 1000)
  }, []);

  // [] means like componentDidMount

  return !spinner && <div>Your content</div>;
};

export default App;
于 2019-04-05T15:38:06.617 回答
6

只需在标签内添加内容<div id="root"></div>,您就可以开始使用了!

// Example:

<div id="root">
   <div id="pre-loader">
        <p>Loading Website...</p>
        <img src="/images/my-loader.gif" />
   </div>
</div>

<App />加载后,React 将自动忽略标签内的所有内容,<div id="root">并用您的实际应用程序覆盖它!

于 2021-10-12T15:39:03.533 回答
5

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

这就是我想出的:

index.html文件

内部 head标签 - 指标样式:

<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会被隐藏并对其应用hidden属性。

更重要的是添加 - !spinner.hasAttribute('hidden')条件阻止在hidden每个组件安装时向微调器添加属性,因此实际上它只会添加一次,当整个应用程序加载时。

于 2017-10-18T10:28:13.887 回答
5

我正在使用react-progress-2 npm 包,它是零依赖的,在 ReactJS 中效果很好。

https://github.com/milworm/react-progress-2

安装:

npm install react-progress-2

将 react-progress-2/main.css 包含到您的项目中。

import "node_modules/react-progress-2/main.css";

包括react-progress-2并将其放在顶部组件中的某个位置,例如:

import React from "react";
import Progress from "react-progress-2";

var Layout = React.createClass({
render: function() {
    return (
        <div className="layout">
            <Progress.Component/>
                {/* other components go here*/}
            </div>
        );
    }
});

现在,每当您需要显示指标时,只需调用Progress.show(),例如:

loadFeed: function() {
    Progress.show();
    // do your ajax thing.
},

onLoadFeedCallback: function() {
    Progress.hide();
    // render feed.
}

请注意,showhide调用是堆叠的,因此在连续 n 次显示调用后,您需要执行 n 次隐藏调用以隐藏指示器,或者您可以使用Progress.hideAll().

于 2018-05-23T20:59:45.183 回答
5

在 componentDidMount 中设置超时有效,但在我的应用程序中我收到了内存泄漏警告。尝试这样的事情。

constructor(props) {
    super(props)
    this.state = { 
      loading: true,
    }
  }
  componentDidMount() {
    this.timerHandle = setTimeout(() => this.setState({ loading: false }), 3500); 
  }

  componentWillUnmount(){
    if (this.timerHandle) {
      clearTimeout(this.timerHandle);
      this.timerHandle = 0;
    }
  }
于 2018-07-23T00:30:40.947 回答
4

你可以通过在 react 中使用延迟加载来轻松做到这一点。为此,您必须使用惰性和悬念来做出这样的反应。

import React, { lazy, Suspense } from 'react';

const loadable = (importFunc, { fallback = null } = { fallback: null }) => {
  const LazyComponent = lazy(importFunc);

  return props => (
    <Suspense fallback={fallback}>
      <LazyComponent {...props} />
    </Suspense>
  );
};

export default loadable;

之后像这样导出您的组件。

export const TeacherTable = loadable(() =>
  import ('./MainTables/TeacherTable'), {
    fallback: <Loading />,
  });

然后在您的路线文件中像这样使用它。

 <Route exact path="/app/view/teachers" component={TeacherTable} />

就是这样,每次你的 DOM 渲染你的加载组件时,你都可以按照我们在上面的后备属性中指定的那样显示。只需确保您仅在componentDidMount()中执行任何 ajax 请求

于 2020-11-13T05:16:38.567 回答
4

这是我的实现,基于答案

./public/index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <title>React App</title>
  <style>
    .preloader {
      display: flex;
      justify-content: center;
    }

    .rotate {
      animation: rotation 1s infinite linear;
    }

    .loader-hide {
      display: none;
    }

    @keyframes rotation {
      from {
        transform: rotate(0deg);
      }

      to {
        transform: rotate(359deg);
      }
    }
  </style>
</head>

<body>
  <div class="preloader">
    <img src="https://i.imgur.com/kDDFvUp.png" class="rotate" width="100" height="100" />
  </div>
  <div id="root"></div>
</body>

</html>

./src/app.js

import React, { useEffect } from "react";

import "./App.css";

const loader = document.querySelector(".preloader");

const showLoader = () => loader.classList.remove("preloader");
const addClass = () => loader.classList.add("loader-hide");

const App = () => {
  useEffect(() => {
    showLoader();
    addClass();
  }, []);
  return (
    <div style={{ display: "flex", justifyContent: "center" }}>
      <h2>App react</h2>
    </div>
  );
};

export default App;

于 2020-08-22T05:43:49.070 回答
4

我也在我的应用程序中使用 React。对于我使用 axios 拦截器的请求,制作加载程序屏幕的好方法(如您展示的示例中的完整页面)是将类或 id 添加到拦截器内的例如主体(此处代码来自官方文档和一些自定义代码):

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
     document.body.classList.add('custom-loader');
     return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
       document.body.classList.remove('custom-loader');
       return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  }); 

然后只需在 CSS 中使用伪元素实现您的加载器(或将类或 id 添加到不同的元素,而不是您喜欢的主体) - 您可以将背景颜色设置为不透明或透明等......示例:

custom-loader:before {
    background: #000000;
    content: "";
    position: fixed;
    ...
}

custom-loader:after {
    background: #000000;
    content: "Loading content...";
    position: fixed;
    color: white;
    ...
}
于 2018-12-21T09:41:28.200 回答
4

我不知道现在回答是否为时已晚,因为您可能已经找到了解决方案,但这是我为将来的人准备的一个问题,因为这个问题确实很有用。:
我在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,你可以把你自己的 API 放在 fetch 中。

于 2021-09-14T07:01:42.467 回答
3

编辑公用文件夹中的 index.html 文件位置。将您的图像复制到与公用文件夹中的index.html相同的位置。然后将 index.html 中包含标签的部分内容替换为 <div id="root"> </div>下面给出的 html 代码。

<div id="root">  <img src="logo-dark300w.png" alt="Spideren" style="vertical-align: middle; position: absolute;
   top: 50%;
   left: 50%;
   margin-top: -100px; /* Half the height */
   margin-left: -250px; /* Half the width */" />  </div>

在加载过程中,徽标现在将出现在页面中间。然后将在几秒钟后被 React 替换。

于 2019-02-12T10:02:57.730 回答
3

如果您正在使用react-router管理应用程序的路由,您可以使用我制作的react-router-loading库轻松添加加载屏幕。

它也会影响页面切换,但我认为如果要预加载第一页,那么预加载其他页面也是很自然的。

反应路由器加载

此方法与此方法之间的区别在于Suspense,使用此库,您可以在获取数据等时继续加载。基本上这种方法与在组件中使用状态非常相似isLoading,但如果您有很多不同的页面,则更容易实现。

用法

在您的路由器部分导入SwitchRoute从而react-router-loading不是react-router-dom

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

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

loading为切换前必须加载的每条路由添加道具

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

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

在带有prop的路由中提到的组件中添加loadingContext.done()初始加载方法的末尾(在本例中为)loadingPage1

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>
于 2021-10-02T12:20:22.317 回答
3

你不需要那么多努力,这是一个基本的例子。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="theme-color" content="#000000" />
  <meta name="description" content="Web site created using create-react-app" />
  <link rel="apple-touch-icon" href="logo192.png" />
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
  <title>Title</title>
  <style>
    body {
      margin: 0;
    }

    .loader-container {
      width: 100vw;
      height: 100vh;
      display: flex;
      overflow: hidden;
    }

    .loader {
      margin: auto;
      border: 5px dotted #dadada;
      border-top: 5px solid #3498db;
      border-radius: 50%;
      width: 100px;
      height: 100px;
      -webkit-animation: spin 2s linear infinite;
      animation: spin 2s linear infinite;
    }

    @-webkit-keyframes spin {
      0% {
        -webkit-transform: rotate(0deg);
      }

      100% {
        -webkit-transform: rotate(360deg);
      }
    }

    @keyframes spin {
      0% {
        transform: rotate(0deg);
      }

      100% {
        transform: rotate(360deg);
      }
    }

  </style>
</head>

<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root">
    <div class="loader-container">
      <div class="loader"></div>
    </div>
  </div>
</body>

</html>

你可以玩弄它HTMLCSS让它看起来像你的例子。

于 2019-08-31T10:57:40.907 回答
3

这个问题可以通过 React 的惰性特性轻松解决。

import { Suspense, lazy } from "react"
import Loading from "components/Loading"

const Dashboard = lazy(() => import("containers/Dashboard"))

const App = () => (
  <Suspense fallback={<Loading />}>
    <Dashboard />
  </Suspense>
)

export default App

当 Dashboard 组件仍在加载时,将显示加载组件。

于 2021-10-01T09:28:41.820 回答
2

使用 Pace 怎么样?

在此处使用此链接地址。

https://github.hubspot.com/pace/docs/welcome/

1.在他们的网站上选择你想要的样式并粘贴到 index.css

2.转到 cdnjs 复制 Pace Js 的链接并添加到 public/index.html 中的脚本标签

3.它会自动检测网络负载并在浏览器顶部显示速度。

您还可以在 css 中修改高度和动画。

于 2020-04-09T09:13:12.890 回答
2

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

我添加到@Ori 答案是在body 标签的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>
于 2020-02-17T06:30:48.933 回答
2

react app的启动是基于主包下载的。React 应用程序仅在浏览器中下载主包后启动。在延迟加载架构的情况下也是如此。但事实是我们无法准确说明任何捆绑包的名称。因为当你运行“npm run build”命令时,webpack 会在每个包的末尾添加一个哈希值。当然我们可以通过更改哈希设置来避免这种情况,但它会严重影响浏览器中的缓存数据问题。由于捆绑名称相同,浏览器可能不会采用新版本。. 我们需要一个 webpack + js + CSS 的方法来处理这种情况。

更改 public/index.html 如下

<!DOCTYPE html>
<html lang="en" xml:lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=3.0, shrink-to-fit=no">
  <meta name="theme-color" content="#000000">
  <!--
      manifest.json provides metadata used when your web app is added to the
      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
    -->
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
  <style>
 .percentage {
      position: absolute;
      top: 50%;
      left: 50%;
      width: 150px;
      height: 150px;
      border: 1px solid #ccc;
      background-color: #f3f3f3;
      -webkit-transform: translate(-50%, -50%);
          -ms-transform: translate(-50%, -50%);
              transform: translate(-50%, -50%);
      border: 1.1em solid rgba(0, 0, 0, 0.2);
      border-radius: 50%;
      overflow: hidden;
      display: -webkit-box;
      display: -ms-flexbox;
      display: flex;
      -webkit-box-pack: center;
          -ms-flex-pack: center;
              justify-content: center;
      -webkit-box-align: center;
          -ms-flex-align: center;
              align-items: center;
    }

    .innerpercentage {
      font-size: 20px;
    }
  </style>
  <script>
    function showPercentage(value) {
      document.getElementById('percentage').innerHTML = (value * 100).toFixed() + "%";
    }
    var req = new XMLHttpRequest();
    req.addEventListener("progress", function (event) {
      if (event.lengthComputable) {
        var percentComplete = event.loaded / event.total;
        showPercentage(percentComplete)
        // ...
      } else {
        document.getElementById('percentage').innerHTML = "Loading..";
      }
    }, false);

    // load responseText into a new script element
    req.addEventListener("load", function (event) {
      var e = event.target;
      var s = document.createElement("script");
      s.innerHTML = e.responseText;
      document.documentElement.appendChild(s);
      document.getElementById('parentDiv').style.display = 'none';

    }, false);

    var bundleName = "<%= htmlWebpackPlugin.files.chunks.main.entry %>";
    req.open("GET", bundleName);
    req.send();

  </script>
  <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->

  <title>App Name</title>
  <link href="<%= htmlWebpackPlugin.files.chunks.main.css[0] %>" rel="stylesheet">
</head>

<body>
  <noscript>
    You need to enable JavaScript to run this app.
  </noscript>
  <div id="parentDiv" class="percentage">
    <div id="percentage" class="innerpercentage">loading</div>
  </div>
  <div id="root"></div>
  <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
</body>

</html>

在您的生产 webpack 配置中,将 HtmlWebpackPlugin 选项更改为以下

 new HtmlWebpackPlugin({
          inject: false,
...

您可能需要使用“弹出”命令来获取配置文件。最新的 webpack 可能有选项来配置 HtmlWebpackPlugin 而不弹出项目。 在此处输入图像描述

于 2019-07-18T11:52:02.650 回答
1

最重要的问题是:“加载”是什么意思?如果您谈论的是要安装的物理元素,那么这里的一些第一个答案很棒。但是,如果您的应用程序首先要做的是检查身份验证,那么您真正加载的是来自后端的数据,无论用户是否传递了一个将他们标记为授权或未授权用户的 cookie。

这是基于 redux 的,但您可以轻松地将其更改为普通的反应状态模型。

动作创建者:

export const getTodos = () => {
  return async dispatch => {
    let res;
    try {
      res = await axios.get('/todos/get');

      dispatch({
        type: AUTH,
        auth: true
      });
      dispatch({
        type: GET_TODOS,
        todos: res.data.todos
      });
    } catch (e) {
    } finally {
      dispatch({
        type: LOADING,
        loading: false
      });
    }
  };
};

finally 部分表示用户是否经过身份验证,收到响应后加载屏幕消失。

这是加载它的组件的样子:

class App extends Component {
  renderLayout() {
    const {
      loading,
      auth,
      username,
      error,
      handleSidebarClick,
      handleCloseModal
    } = this.props;
    if (loading) {
      return <Loading />;
    }
    return (
      ...
    );
  }

  ...

  componentDidMount() {
    this.props.getTodos();
  }

...

  render() {
    return this.renderLayout();
 }

}

如果 state.loading 是真的,我们总是会看到一个加载屏幕。在 componentDidMount 上,我们调用我们的 getTodos 函数,它是一个动作创建者,当我们收到响应(可能是错误)时,它会将 state.loading 变为 falsy。我们的组件更新,再次调用渲染,这次没有加载屏幕,因为 if 语句。

于 2019-04-04T16:32:24.187 回答