网络日志

Vue.js/Nuxt.js在页面加载完成前显示Loading动画

前言

最近接到一个项目,使用Nuxt.js写了一个网页,但是在加载网页时发生以下情怳:这里用比卡超代替公司logo,也懒得去背,这不是重点(doge)

这样就很不美观,於是增加了Loading动画:

思路

增加div,用css写loading画面,然后使用js进行加载判断。这次就使用document.readyState来判断网页里的元素是否完全加载完成。

The document.readyState property can return these three string values:

loading: when the document is still loading.

interactive: when the document has finished loading but sub-resources such as stylesheets, images and frames are still loading.

complete: when the document and all sub-resources have finished loading.

document.readyState !== complete时,网页未加载完成,把网页的body隠藏,把div显示出来,这时在屏幕显示的是Loading动画;当加载完成时,此时document.readyState === complete,把div隠藏,把body显示出来。

在Vue.js设定Loading动画

前住public/index.html

代码部分:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>icon.png">
    <title><%= htmlWebpackPlugin.options.title %></title>
      <style>

          body {
              margin: 0;
              line-height: inherit;
          }

          *, ::before, ::after {
              box-sizing: border-box;
              border-width: 0;
              border-style: solid;
              border-color: #e5e7eb;
          }

          #loader {
              border: 12px solid #f3f3f3;
              border-radius: 50%;
              border-top: 12px solid #444444;
              width: 70px;
              height: 70px;
              animation: spin 1s linear infinite;
          }

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

          .center {
              position: absolute;
              top: 0;
              bottom: 0;
              left: 0;
              right: 0;
              margin: auto;
          }
      </style>
  </head>
  <body>
    <div id="loader" class="center"></div>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script>
        document.onreadystatechange = function() {
            if (document.readyState !== "complete") {
                document.querySelector(
                    "body").style.visibility = "hidden";
                document.querySelector(
                    "#loader").style.visibility = "visible";
            } else {
                document.querySelector(
                    "#loader").style.display = "none";
                document.querySelector(
                    "body").style.visibility = "visible";
            }
        };
    </script>
  </body>
</html>

在Nuxt.js设Loading动画

在项目的根目录下的app.html,没有的话自己建立

代码部分:

<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
  {{ HEAD }}
  <style>
    #loader {
      border: 12px solid #f3f3f3;
      border-radius: 50%;
      border-top: 12px solid #444444;
      width: 70px;
      height: 70px;
      animation: spin 1s linear infinite;
    }

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

    .center {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      margin: auto;
    }
  </style>
</head>
<body {{ BODY_ATTRS }}>
<div id="loader" class="center"></div>
{{ APP }}
<script>
  document.onreadystatechange = function() {
    if (document.readyState !== "complete") {
      document.querySelector(
        "body").style.display = "none";
      document.querySelector(
        "#loader").style.display = "block";
    } else {
      document.querySelector(
        "#loader").style.display = "none";
      document.querySelector(
        "body").style.display = "block";
    }
  };
</script>
</body>
</html>

如何测试

有读者可能会有想到说,如果网页加载过快,那就看不到效果了,怎么办?

其实在edge/chrome中,按F12进入主控台,然后选择网络,选择慢速3G

Q&A

我打算把在我在写loading画面时所遇到的问题直接做成Q&A,或许可以解答读者在阅读这篇文章时的疑惑。

Q1: 为什么不直接用Vue的onMounted()等生命周期钓子来写loading动画?A1: 这个我试过,不行。Vue加载网页的流程是:首先打开public/index.html,然后在里面加载Vue组件,从最外层判断文档加载状态才好做。如果把loading的div写进Vue文件里的话,会出现了组件正在加载时才开始判断document.readyState的状态。变相两个都一起加载,从画面上来看就是叠加在一起,然后等onMounted()结束后判断document.readyState的事件结束,也就是loading画面已经消息时,页面还在加载,导致loading的div和body出现和消失的时间点不对,效果不成功。

Q2:其实Nuxt.js有.nuxt/views/app.template.html,为什么不直接在里面写loading动画,而是要在项目的根目录下增加app.html?A2:我这个项目是静态打包的,直接改app.template.html的代码,它并不会打包进来。

Q3:Nuxt.js有.nuxt/layouts/default.vue文件,为什么不直接在里面写loading动画?A3:它始终是Vue的文件,也是Vue的一部份,并不是最外层,如果在这个文件写loading动画的话会出现和Q1相同的结果。

Q4:其实有人研发了Vue的Loading组件,例如vue-loading-overlay,为什么不使用它?A4:

  • 不会用,懒得研究
  • 不想用,不可能为了一个小小的loading动画而去npm引用一个组件回来
  • 根据我的理解,这个可能是给api调用等待数据提交或返回时专门所用的组件,它并没有判断文档加载的功能在里面,逻辑还是要自已写 没理解错的话应该是这样

资料参考

How to show Page Loading div until the page has finished loading? -- GeeksForGeekshttps://www.geeksforgeeks.org...

Views -- NuxtJShttps://nuxtjs.org/docs/conce...