这是来自Google Adsense应用程序页面的示例。在主页之前显示的加载屏幕在之后显示。
我不知道如何使用React来做同样的事情,因为如果我使加载屏幕由React组件呈现,它不会在页面加载时显示,因为它必须等待DOM呈现。
更新时间:
我通过将屏幕加载程序放入index.html
并在React componentDidMount()
生命周期方法中将其删除来举例说明了我的方法。
这是来自Google Adsense应用程序页面的示例。在主页之前显示的加载屏幕在之后显示。
我不知道如何使用React来做同样的事情,因为如果我使加载屏幕由React组件呈现,它不会在页面加载时显示,因为它必须等待DOM呈现。
更新时间:
我通过将屏幕加载程序放入index.html
并在React componentDidMount()
生命周期方法中将其删除来举例说明了我的方法。
Answers:
可以通过将加载图标放置在html文件中(例如index.html)来完成操作,以便用户在html文件加载后立即看到该图标。
当您的应用程序完成加载后,您只需在生命周期挂钩中删除该加载图标即可,我通常在中执行此操作componentDidMount
。
呈现html页面时,立即显示微调器(在React加载时),并在React准备好后将其隐藏。
由于微调器是用纯HTML / CSS呈现的(在React域之外),因此React不应直接控制显示/隐藏过程,并且实现应对React透明。
由于您将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>
例如)将不起作用。
一种解决方法是将Spinner类添加到react容器,并使用:empty
伪类。只要没有任何内容呈现到容器中,微调器就会可见(注释不计算在内)。一旦react提供注释以外的内容,加载程序就会消失。
例子1
在示例中,您可以看到一个null
在准备就绪之前将进行渲染的组件。容器也是加载器- <div id="app" class="app"></div>
,并且加载器的类只有在容器加载器的类才起作用:empty
(请参见代码中的注释):
例子2
使用:empty
伪类显示/隐藏选择器的一种变体是将微调器设置为应用容器的同级元素,并使用相邻的同级组合器(+
)在容器为空的情况下显示它:
要对微调器的显示状态进行更细粒度的控制,请创建两个函数showSpinner
和hideSpinner
,然后将它们通过props传递到根容器。这些函数可以操纵DOM,或执行控制微调器所需的任何操作。这样,React不会意识到“外部世界”,也不需要直接控制DOM。您可以轻松地替换用于测试的功能,或者如果您需要更改逻辑,则可以将其传递给React树中的其他组件。
例子1
示例2-钩子
此示例使用useEffect
挂钩在组件安装后隐藏微调器。
解决方法是:
在渲染函数中执行以下操作:
constructor() {
this.state = { isLoading: true }
}
componentDidMount() {
this.setState({isLoading: false})
}
render() {
return(
this.state.isLoading ? *showLoadingScreen* : *yourPage()*
)
}
在构造函数中将isLoading初始化为true,在componentDidMount上将其初始化为false
如果有人在寻找上述用例的嵌入式,零配置和零依赖库,请尝试一下speed.js(http://github.hubspot.com/pace/docs/welcome/)。
它会自动挂接到事件(ajax,readyState,历史记录pushstate,js事件循环等)并显示可自定义的加载器。
与我们的React / Relay项目配合良好(使用react-router处理中继更改,中继请求) (不隶属;为我们的项目使用了speed.js,效果很好)
public/index.html
并选择样式即可。这简直是简单,令人惊叹的插件。谢谢。
当您的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,事情就不会那么复杂了,因为用户在页面加载后便会真正看到真实的应用程序。
这将在ReactDOM.render()
控制根 之前发生<div>
。也就是说,您的应用尚未安装完成。
因此,您可以将加载程序添加index.html
到根目录下的文件中<div>
。这将在屏幕上显示,直到React接管为止。
您可以使用最适合您的任何加载器元素(svg
例如动画)。
您无需在任何生命周期方法中将其删除。正如我们在下面的GIF中看到的那样,React将用您的render 替换其根目录的 所有子级。<div>
<App/>
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
使用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);
如今,我们还可以在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;
在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;
}
}
我最近不得不处理该问题,并想出了一个解决方案,对我来说效果很好。但是,我在上面尝试了@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
文件内部(在render函数中):
const spinner = document.getElementById('spinner');
if (spinner && !spinner.hasAttribute('hidden')) {
spinner.setAttribute('hidden', 'true');
}
如何运作?
当第一个组件(在我的应用程序app.js
中大多数情况下也是)正确安装时,该组件spinner
将被隐藏,并带有apply hidden
属性。
更为重要的是添加-
!spinner.hasAttribute('hidden')
条件阻止hidden
每次安装组件时都向微调器添加属性,因此,当整个应用加载时,实际上只会添加一次。
我正在使用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.
}
请注意,show
和hide
呼叫是堆叠在一起的,因此在n次连续的show呼叫之后,您需要执行n hide呼叫来隐藏指示器,或者可以使用Progress.hideAll()
。
我也在我的应用程序中使用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;
...
}
请编辑您的index.html文件位置的公共文件夹。将图像复制到公用文件夹中与index.html相同的位置。然后将包含<div id="root"> </div>
标签的index.html内容的一部分替换为 以下给定的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替换。
您不需要那么多努力,这是一个基本示例。
<!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>
你可以玩的HTML
,并CSS
使它看起来像你的榜样。
最重要的问题是:“加载”是什么意思?如果您正在谈论要安装的物理元素,那么这里的第一个答案很好。但是,如果您的应用程序要做的第一件事是检查身份验证,那么您真正加载的是来自后端的数据,无论用户是否通过了将其标记为授权或未授权用户的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
});
}
};
};
最后一部分表示是否对用户进行了身份验证,在收到响应后加载屏幕将消失。
这是加载组件的样子:
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。我们的组件将更新,再次调用render,由于if语句,这一次没有加载屏幕。
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而无需弹出项目。
我还使用了@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>
怎样使用Pace
在此使用此链接地址。
https://github.hubspot.com/pace/docs/welcome/
1.在他们的网站上选择所需的样式并粘贴到index.css中
2.go to cdnjs复制Pace Js的链接并添加到public / index.html中的脚本标签中
3.它会自动检测网络负载并在浏览器顶部显示速度。
您还可以在CSS中修改高度和动画。