记录–Vue自定义指令实现加载中效果v-load(不使用Vue.extend)

  • 记录–Vue自定义指令实现加载中效果v-load(不使用Vue.extend)已关闭评论
  • 93 次浏览
  • A+
所属分类:Web前端
摘要

网站效果演示:ashuai.work:8888/#/myLoadGitHub仓库地址代码:github.com/shuirongshu…实现加载中效果,一般有两种方式:


这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录--Vue自定义指令实现加载中效果v-load(不使用Vue.extend)

记录--Vue自定义指令实现加载中效果v-load(不使用Vue.extend)

网站效果演示:ashuai.work:8888/#/myLoad

GitHub仓库地址代码:github.com/shuirongshu…

加载中思路分析

实现加载中效果,一般有两种方式:

  • 第一种是:搞一个load组件,然后使用Vue.extend()方法去继承一个加载组件去使用,比如笔者的这篇文章:juejin.cn/post/702172…
  • 第二种是:直接使用指令去在需要加载的dom上去创建一个加载中的dom元素,并指定相应的样式即可。本篇文章说的是第二种。

我们先看一下效果图

v-load效果图

记录--Vue自定义指令实现加载中效果v-load(不使用Vue.extend)

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

假设我有一个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元素。

关于自定义指令的入门基础知识可以看官方文档,或者参见笔者之前的关于自定义指令的文章:juejin.cn/post/702960…

实现步骤二:给目标元素创建一个子元素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>

本文转载于:

https://juejin.cn/post/7182375025368891429

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--Vue自定义指令实现加载中效果v-load(不使用Vue.extend)