React-экран загрузки дисплея во время рендеринга DOM?
это пример со страницы приложения Google Adsense. Экран загрузки, отображаемый перед главной страницей, отображался после.
Я не знаю, как сделать то же самое с 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;
}
}