這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 網站效果演示:ashuai.work:8888/#/myLoad GitHub倉庫地址代碼:github.com/shuirongshu… 載入中思路分析 實現載入中效果,一般有兩種方式: 第一種是:搞一個load組件,然後使用Vue.e ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
網站效果演示:ashuai.work:8888/#/myLoad
GitHub倉庫地址代碼:github.com/shuirongshu…
載入中思路分析
實現載入中效果,一般有兩種方式:
- 第一種是:搞一個
load組件
,然後使用Vue.extend()方法
去繼承一個載入組件去使用,比如筆者的這篇文章:juejin.cn/post/702172… - 第二種是:直接使用指令去在需要載入的
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
元素。
關於自定義指令的入門基礎知識可以看官方文檔,或者參見筆者之前的關於自定義指令的文章: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>