自定义加载条

96
吃柠檬的刺猬
2017.07.03 14:54* 字数 1594

在页面加载前新增 loading 状态的几种方式

loading 加载分为好几种,一种是含百分比计算的,一种是不含百分比。
含百分比的需要进行计算,有的网站是通过定义不同的节点来进行计算,比如执行了一个 ajax 结束算一个节点,进度条增加百分之三十,再执行完一个再加百分之三十。这个方法我自己也没有去实际做过,只是查资料了解到的,所以本文对此不作介绍。
不含百分比的就比较简单,只需要放一个动画就可以。

先记录一下基本步骤

  1. 将 loading 页和主页面分开加载,因为所有的文件在主页面都打包成了一个 js 文件,所以单独将 loading 拿出来加载,处理方式是修改 webpack 打包文件
 entry: {     
      main: './main/index.js', // 原本的主页面文件     
     loading: './loading/index.js', // 新增的 loading 页面文件   
},
  1. 在 index.html 文件中,引入 loading.js 文件,引入的文件一定要放在 main.js 之前,因为要让浏览器先加载 loading 文件
<body> 
    <main id="application-container"></main>   <!--在 main.js 之前引入 loading.js 文件-->  
    <script src="/demo/static/apps/entry/loading.js"></script>   
    <script src="/demo/static/apps/entry/main.js"></script>
</body>
  1. 编辑 loading.js 文件内容,这才是重点的地方
    我们的想法是先加载 loading 的动画,当检测到 dom 已经加载完毕以后,移除掉 loading 动画,加载主页面
    此处需要下载 document-ready 库用于检测 document.ready 事件。
import ready from 'document-ready';
const loader = document.createElement('div');
const body = document.getElementsByTagName('body')[0] 
const progress = '<div>正在加载啦啦啦啦</div>'; // loading 页内容
loader.innerHTML = progress; body.appendChild(loader) 
// 上面的内容基本就是在做一件事,就是创建新元素插入到 body 中
ready(() => {
 // 当 document.ready 时,移除元素 
body.removeChild(loader)
})

基本的步骤就是上述部分,但是很多时候我们并不希望只是一个简陋的元素展示出来,希望有加载条或者一些动画等等。下面分三种情况说明一下。

loading 动画

  1. 动图
    直接使用 gif 其实是最简单的部分,用 gif 替代普通的元素即可,如
    const progress = <div>正在加载啦啦啦啦![](demo.gif)</div>;
    再加一些样式即可,其实如果 dom 元素太多的话,可以另用一个文件去专门写布局和样式,然后再导入到 loading.js 中。

  2. 含百分比加载条,现成的轮子:pace.js,可以直接下载 pace.js 以及它的主题,然后在 html 中引入,官网上写得很清楚了。另外一种方式是 npm 下载了,这里有两个地方要注意。

    • 下载的包名并不是 pace,而是 pace-js,yarn add pace-js 下载的才是正确的包
    • 引入 pace 模块的时候有 bug,无法直接通过 import pace from 'pace-js' 引入 pace module,原因是 pace.js 中的 AMD define 时出错了,它将 pace 定义为依赖,但是 pace 在 npm 中是另外的一个不关联 CLI 进度条包的库,也就是说和这个库没有什么关系,具体解决方法:
      在 webpack 中新增下面这条语句
{ 
  test: require.resolve("pace-progress"), 
  loader: "imports?define=>false" 
}

下载 imports-loader 包:yarn add imports-loader,然后就解决了。

原因和解决方法都是参考这个 issue:[我是解决问题的 issue](https://github.com/HubSpot/pace/issues/328)

解决这些以后,就可以直接使用 pace 了,但是一定要记得进入 pace 的样式包,当然也可以自己写,根据以下的 class 写自己指定的样式,也可以直接使用 pace 文档中已有的样式。

<div class="pace pace-inactive">
  <div class="pace-progress" data-progress-text="100%" data-progress="99" style="transform: translate3d(17%, 0px, 0px);"> 
  <div class="pace-progress-inner" style="   background: black;">
</div>
</div>
  <div class="pace-activity"></div>
</div>

pace.start() 开始加载进度条,pace.stop() 则停止加载,它没有 done 方法,凡事可以监听 done 事件,在加载执行完成时触发的事件,方法如下:

 pace.on("done",function(){ 
  console.log('im done!');
})

使用中遇到的问题:使用 pace 的过程中发现一个问题,pace 执行 stop() 时貌似是异步的,如果将 pace.stop() 放入 ready(){} 中执行,loading 页面的加载条还未结束,或者结束还未消失,主页面已经出现,导致加载条覆盖住了主页面。所以也许 pace 的用法是需要增加一个 mask 遮罩层或者仅仅用在 ajax 上比较合理?(虽然不仅仅能用在 ajax 上)。

  1. 使用 nprogress
    nprogress 类似于 pace,但是没有出现 pace 出现的上述多个问题,而且它没有自带主题,貌似只有一个固有样式,但是我们仍然可以对它进行自定义。
    用法如下:
  • yarn add nprogress

  • 引入 Nprogress 模块,也要记得引入样式

import NProgress from 'nprogress';
import 'nprogress/nprogress.css'; 

在需要的地方执行 Nprogress.start() 开始加载进度条,在任何需要的地方执行 Nprogress.done() 结束进度条,展示出来的效果就是它的默认样式了。

我们可以对它的样式进行自定义,或者干脆对它的DOM元素进行自定义,它原本的结构如下:

<div id="nprogress"> 
  <div class="bar" role="bar" style="transform: translate3d(-73.2731%, 0px, 0px); transition: all 200ms ease;">   
    <div class="peg"></div> 
  </div> 
  <div class="spinner" role="spinner">   
    <div class="spinner-icon"></div> 
  </div>
</div>

通过以上结构是默认结构,我们可以自己写一份 css,然后引入到 loading/index.js 文件当中。
或者可以通过以下方式自定义结构,

const progress = `<div id="nprogress">
<div class="bar" role="bar" style="background: red; transform: translate3d(-0.6%, 0px, 0px); transition: all 200ms ease;">
<div class="peg" style="box-shadow: 0 0 10px #fff, 0 0 5px #fff;"></div>
</div>
<div class="spinner" role="spinner">
<div class="spinner-icon" style="border-top-color:white;border-left-color: white"></div>
</div>
</div>`;
NProgress.configure({  template: progress, });

这只是一个例子,progress 的内容可以单独作为一个文件,再 import 进来,需要注意几个地方:
configure 顾名思义是对 Nprogress 进行设置,所以要放在 Nprogress.start() 之前。

在自定义结构时,要给一些元素指定role属性,这个是必须要指定的,不然 nprogress 不清楚哪个元素该做什么。比如:

role="bar" //表示的是加载条
role="spinner" //表示的是原本样式中右上角的旋转,具体叫什么我也说不上来...

在本处,我们就通过如下方式来进行:

// loading/index.js
import NProgress from 'nprogress'; // 引入 Nprogress 模块
import progress from './demo/index'; // 自定义的 dom
NProgress.configure({ 
// 设置自己定义的dom
 template: progress,});
    NProgress.start(); // 加载进度条
ready(() => { 
    NProgress.done(); // 结束并移除进度条
})

progress 同样遇到 pace 一样的问题,即 loading 内容还没消失,主页面内容就已经显示了,document.ready 的节点执行 nprogress.done() 的操作,首先滚动条到滚动到达最底的位置,然后再让 loading 消失,这几个步骤需要一定的时间,所以会出现这种情况。pace 刚才的问题应该同样是这个原因。只能让 template 尽量简洁,减少渲染时间。
我个人更倾向于用 nprogress。如果喜欢 pace 的主题又想用 progress 的,可以直接去参考样式,内容并不多,毕竟两者的 dom 结构比较相似,样式名换一下再稍微修改修改应该就能用起来。

当然如果想要更多自己定制的内容,还是需要去看 pace 或者 progress 官网,链接如下:
pace
progress

项目