网站成果演示:http://ashuai.work:8888/#/myLoad
GitHub仓库地址代码:https://github.com/shuirongsh...

加载中思路剖析

实现加载中成果,个别有两种形式:

  • 第一种是:搞一个load组件,而后应用Vue.extend()办法去继承一个加载组件去应用,比方笔者的这篇文章:https://segmentfault.com/a/11...
  • 第二种是:间接应用指令去在须要加载的dom下来创立一个加载中的dom元素,并指定相应的款式即可。本篇文章说的是第二种。

咱们先看一下效果图

v-load效果图

实现步骤一:加上自定义指令

假如我有一个dom元素,我给其加上一个自定义的指令v-load="loading",绑定一个具体的布尔值loading,用于管制开启加载中和敞开加载中

<div class="box" v-load="loading">111</div>loading: true.box {  width: 160px;  height: 80px;  border: 2px solid #666;}

接下来,我就要在自定义指令的相干钩子函数中去操作这个dom元素。

对于自定义指令的入门基础知识能够看官网文档,或者参见笔者之前的对于自定义指令的文章:https://segmentfault.com/a/11...

实现步骤二:给指标元素创立一个子元素dom用于加载

在自定义指令的初始化bind钩子函数中,咱们能够拿到这个dom元素,首先给这个指标元素开始绝对定位,让用于加载中的子元素dom去相对定位,以这个绝对定位的父元素进行参考

bind(el, binding) {    const target = el;    // 父元素绝对定位    target.style.position = "relative";    // 子元素遮罩层局部    let loadChild = document.createElement("div");    loadChild.className = "loadClass";}

上述代码中给加载中的子元素loadChild指定一个款式类名loadClass

在这里小伙伴可能有疑难了,这个自定义指令的款式怎么写呢?自定义指令中也没有style标签啊?

是的,自定义指令中不能间接写款式,不过没关系,咱们能够先写好一个css款式,而后引入过去应用啊,如下:

// 引入拆分的款式,便于自定义指令中应用import './index.css'bind(el, binding) {    ......    loadChild.className = "loadClass";}

这样的话,className的款式,能够在引入的同级目录下的./index.css文件中设置了,loadClass款式如下:

.loadClass {    /* 宽高百分百 */    position: absolute;    top: 0;    bottom: 0;    left: 0;    right: 0;    /* 默认背景色和色彩 */    background-color: rgba(255, 255, 255, 0.99);    color: #0b6ed0;    /* 透明度过渡应用搭配display:none; */    opacity: 0.8;    transition: all 0.3s;    /* 居中 */     display: flex;    align-items: center;    justify-content: center;}

留神,加载中成果开启和隐没,不必应用vue自带的过渡组件transition,咱们能够应用透明度搭配搭配display:none;去设置

留神,加载中要以父元素为边界去管制,可不能满屏加载哦

而后初始化的时候,看看v-load绑定的值是true还是false,同时加上一个用于暗藏的类名:load-hide。再把这个加载中的dom元素追加到指标父元素身上。

loadChild.classList.add('load-hide') // 增加类名target.appendChild(loadChild); // 追加加载中子元素/* 通过透明度实现过渡动画 */.load-hide {    opacity: 0;}

这样的话,初始化的加载中就做好了。v-load绑定的值是false的时,就暗藏之

实现步骤三:当组件更新时,去增加或移除这个load-hide类名

componentUpdated(el, binding, vnode, oldVnode) {        let flag = binding.value        let loadChild = el.querySelector('.loadClass')        if (flag) { // v-load绑定的值为true,就移除这个类名,就能看到了            loadChild.style.display = 'flex'            setTimeout(() => {                loadChild.classList.remove('load-hide')            }, 0);        } else { // 绑定的值为false时,再增加这个类名,同时暗藏dom            loadChild.classList.add('load-hide')            setTimeout(() => {                loadChild.style.display = 'none'            }, 360);        }    },

留神上述代码中为啥不间接暗藏,而是应用定时器缩短360毫秒再去暗藏,因为笔者设置的加载dom的过渡工夫是0.3秒,即要等到透明度过渡完了当前,再暗藏加载中dom,这样看着就天然一些了。

.loadClass { transition: all 0.3s; }

到这里,咱们的v-load自定义指令的加载中成果,就初步实现了。不过性能有点少,自定义加载中我还想,去动态控制:

  • 自定义加载图标名
  • 自定义加载文字
  • 自定义加载文字色彩
  • 自定义加载背景色

那这样怎么办呢?

实现步骤四:优化自定义指令,反对传入更多的参数

此时,我的v-load自定义指令,就不必绑定一个布尔值了。能够思考绑定一个对象啊,通过管制这个对象的具体值,来动态控制加载中的成果。

  • 原来自定义指令绑定:v-load = loading // typeof loading == 'boolean'
  • 当初自定义指令绑定:v-load = loading2 // typeof loading2 == 'object'
<div class="box" v-load="loading2">222</div>// 如果想要有更多的配置项,就传一个对象,留神要指定字段loading2: {    value: true,    icon: "el-icon-eleme", // 自定义加载图标名    text: "客官稍等哦...", // 自定义加载文字    color: "red", // 自定义加载文字色彩    bgColor: "#baf", // 自定义加载背景色}

传参指定相应字段,自定义指令中接参,就要在相干的钩子中去接管并解决这些参数。

如何解决这些参数?

  • v-load绑定的值的类型的判断,是布尔值,还是对象,执行不同的操作
  • 应用原生js的形式去,发明dom元素、给dom元素指定类名(或增加删除类名)
  • 思考到性能缘故,能够应用文档碎片优化document.createDocumentFragment()
  • 最初丢入遮罩层dom外部即可

残缺代码

自定义指令款式文件index.css

.loadClass {    /* 宽高百分百 */    position: absolute;    top: 0;    bottom: 0;    left: 0;    right: 0;    /* 默认背景色和色彩 */    background-color: rgba(255, 255, 255, 0.99);    color: #0b6ed0;    /* 透明度过渡应用搭配display:none; */    opacity: 0.8;    transition: all 0.3s;    /* 禁止文字抉择 */    user-select: none;    display: flex;    align-items: center;    justify-content: center;}/* 通过透明度实现过渡动画 */.load-hide {    opacity: 0;}.loadClass>i {    margin-right: 4px;}

自定义指令的js文件index.js

留神,自定义指令还须要注册一下能力应用哦

// 引入拆分的款式,便于自定义指令中应用import './index.css'export default {    // 初始化绑定dom钩子函数    bind(el, binding) {        const target = el;        // 传参类型判断变量管制        let flag;        let isObj;        if (typeof binding.value == 'boolean') {            flag = binding.value            isObj = false        }        if (typeof binding.value == 'object') {            flag = binding.value.value            isObj = true        }        // 有dom元素才去做操作        if (target) {            // 父元素绝对定位            target.style.position = "relative";            // 子元素遮罩层局部            let loadChild = document.createElement("div");            loadChild.className = "loadClass";            // 创立文档碎片性能略微优化一点点            let fragment = document.createDocumentFragment()            // 孙子元素1加载图标局部            let iSun = document.createElement("i");            iSun.className = isObj ? binding.value.icon : "el-icon-loading";            // 孙子元素2文字提醒局部            let spanSun = document.createElement("span");            spanSun.innerHTML = isObj ? binding.value.text : '加载中...'            // 应用文档碎片将iSun和spanSun装起来            fragment.appendChild(iSun);            fragment.appendChild(spanSun);            // 将文档碎片丢入子元素遮罩层内            loadChild.appendChild(fragment);            // 款式判断设置            if (isObj) {                loadChild.style.color = binding.value.color                loadChild.style.backgroundColor = binding.value.bgColor            }            // 若为false,就暗藏            if (!flag) {                loadChild.classList.add('load-hide')                loadChild.style.display = 'none'            }            // 父元素增加子元素遮罩层应用            target.appendChild(loadChild);        }    },    // dom组件更新操作控制显示和暗藏    componentUpdated(el, binding, vnode, oldVnode) {        let flag = typeof binding.value == 'boolean' ? binding.value : binding.value.value        let loadChild = el.querySelector('.loadClass')        if (flag) {            loadChild.style.display = 'flex'            setTimeout(() => {                loadChild.classList.remove('load-hide')            }, 0);        } else {            loadChild.classList.add('load-hide')            setTimeout(() => {                loadChild.style.display = 'none'            }, 360);        }    },}

应用自定义load指令的中央

<template>  <div>    <h3>指令形式加载中...</h3>    <br />    <button @click="loadFn">点一下</button>    <br />    <br />    <el-table v-load="loading" border :data="tableData" style="width: 80%">      <el-table-column prop="name" label="姓名"></el-table-column>      <el-table-column prop="age" label="年龄"></el-table-column>      <el-table-column prop="home" label="他乡"></el-table-column>      <el-table-column prop="like" label="喜好"></el-table-column>    </el-table>    <br />    <div class="box" v-load="loading">111</div>    <br />    <div class="box" v-load="loading2">222</div>  </div></template><script>export default {  name: "myLoadName",  data() {    return {      // 自定义指令形式,默认绑定的值是布尔值      loading: true,      // 如果想要有更多的配置项,就传一个对象,留神要指定字段      loading2: {        value: true,        icon: "el-icon-eleme", // 自定义加载图标名        text: "客官稍等哦...", // 自定义加载文字        color: "red", // 自定义加载文字色彩        bgColor: "#baf", // 自定义加载背景色      },      tableData: [        {          name: "孙悟空",          age: 500,          home: "花果山水帘洞",          like: "桃子",        },        {          name: "猪八戒",          age: 88,          home: "高老庄",          like: "肉包子",        },        {          name: "沙和尚",          age: 1000,          home: "通天河",          like: "游泳",        },      ],    };  },  methods: {    loadFn() {      this.loading = !this.loading;      this.loading2.value = !this.loading2.value;    },  },};</script><style lang='less' scoped>.box {  width: 160px;  height: 80px;  border: 2px solid #666;  box-sizing: border-box;}</style>

大家能够去GitHub仓库中看一下笔者的代码,除了自定义指令外,还有别的笔者仿写的组件,或者对大家有一点点的帮忙^_^

欢送大家不吝赐教,不吝star

A good memory is better than a bad pen. Write it down......