React-экран загрузки дисплея во время рендеринга DOM?

это пример со страницы приложения Google Adsense. Экран загрузки, отображаемый перед главной страницей, отображался после.

enter image description here

Я не знаю, как сделать то же самое с React, потому что если я сделаю экран загрузки, отображаемый компонентом React, он не отображается во время загрузки страницы, потому что он должен ждать DOM, отображаемого раньше.

Обновлено:

Я сделал пример моего подхода, поставив загрузчик экрана в index.html и удалите его в React componentDidMount() метод жизненного цикла.

пример: https://nguyenbathanh.github.io

источник: https://github.com/nguyenbathanh/react-loading-screen

8 ответов


Это можно сделать, поместив значок загрузки в HTML-файл (индекс.html for ex), так что пользователи видят значок сразу после загрузки html-файла.

когда ваше приложение завершает загрузку, вы можете просто удалить этот значок загрузки в крючке жизненного цикла, я обычно делаю это в componentDidMount.


так как вы рендеринг реагировать в контейнер DOM -<div id="app"></div>, вы можете добавить счетчик в этот контейнер, и когда react загрузится и отобразится, счетчик исчезнет.

проблема:

React заменит содержимое контейнера, как только ReactDOM.render() называется. Даже если вы делаетеnull, содержимое все равно будет заменено комментарием -<!-- react-empty: 1 -->. Это означает, что если вы хотите отобразить загрузчик во время рендеринга react, загрузчик разметка, размещенная внутри контейнера (<div id="app"><div class="loader"></div></div> например) не будет работать.

устранение:

мое решение-добавить класс загрузчика непосредственно в контейнер, но с помощью :empty псевдо-класс. Счетчик будет виден, пока ничего не отображается в контейнер (комментарии не учитываются). Как только react отобразит элемент, загрузчик исчезнет.


в примере вы можете увидеть компонент что делает null пока не будет готово. Контейнер также является загрузчиком - <div id="app" class="app"></div>, и класс загрузчика будет работать только если это :empty (см. комментарии в коде):

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

  componentDidMount() {
    setTimeout(() => this.setState({ loading: false }), 1500); // simulates an async action, and hides the spinner
  }
  
  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>
    ); 
  }
}

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 -->

вариант использования :empty псевдо-класс для / Показать Скрыть селектор, устанавливает счетчик в качестве родственного элемента в контейнер приложения и показывает его, пока контейнер пуст, используя рядом родственный комбинатор (+):

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

  componentDidMount() {
    setTimeout(() => this.setState({ loading: false }), 2500); // simulates loading of data
  }
  
  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>
    ); 
  }
}

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>

обходной путь для этого:

в вашей функции рендеринга сделайте что-то вроде этого:

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

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

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

инициализировать загрузку как true в конструкторе и false в componentDidMount


Если кто-то ищет библиотеку drop-in, zero-config и zero-dependencies для вышеуказанного варианта использования, попробуйте pace.js (http://github.hubspot.com/pace/docs/welcome/).

он автоматически подключается к событиям (ajax, readyState, history pushstate, JS event loop и т. д.) и показывает настраиваемый загрузчик.

хорошо работал с нашими проектами react / relay (обрабатывает изменения навигации с помощью react-router, запросы реле) (Не affliated; использовали темпами.js для нашего проекты и это сработало отлично)


когда ваше приложение React является массивным, для него действительно требуется время, чтобы встать и запустить после загрузки страницы. Скажем, вы монтируете свою часть React приложения в #app. Обычно это элемент в вашем индексе.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>

после загрузки страницы пользователь сразу увидит исходное содержание индекса.формат html. Вскоре после этого, когда React будет готов подключить всю иерархию отображаемых компонентов к этому узлу DOM, пользователь увидит фактическое приложение.

Примечание class, а не className. Это потому, что вам нужно поместить это в свой html-файл.


если вы используете SSR, все менее сложно, потому что пользователь фактически увидит реальное приложение сразу после загрузки страницы.


недавно мне пришлось иметь дело с этой проблемой и придумать решение, которое отлично работает для меня. Тем не менее, я пробовал @Ori Drori решение выше, и, к сожалению, это не сработало правильно (были некоторые задержки + мне не нравится использование setTimeout функция есть).

вот что я придумал:

внутри head tag-стили для индикатора:

<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 tag:

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

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

а то очень простая логика, внутри app.js file (в функции рендеринга):

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

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

как это работает?

когда первый компонент (в моем приложении это app.js в большинстве случаев) монтируется правильно, то spinner скрывается с применением hidden атрибут к нему.

что более важно добавить - !spinner.hasAttribute('hidden') условие предотвращает добавление на блесну с каждым компонентом гору, так что на самом деле он будет добавлен только один раз, когда приложение загружает.


Я использую реагировать-Прогресс-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.
}

Пожалуйста, обратите внимание, что show и hide вызовы складываются, поэтому после n-последовательных вызовов шоу, вам нужно сделать N скрыть вызовы, чтобы скрыть индикатор или вы можете использовать Progress.hideAll().


установка тайм-аута в 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;
    }
  }