如何實現vue加載指令 v-loading
本文不會詳細的說明 vue 中指令這些知識點,如果存在疑問,請自行查閱文檔或者其他資料
為什么使用指令實現
- 在日常的開發(fā)中,加載效果是非常常見的,但是怎么才能方便的使用,那就還是值得思考一番的,
- 比如在 vue 中,最簡單的方式就是封裝為一個組件了,但是如果封裝為組件的話,在不想注冊為全局組件的時候,每次都需要引入、注冊、使用;如果注冊為全局組件,你也往往需要分析結構在合適的位置插入組件,貌似使用起來都會麻煩一點,loading 這種使用頻率高的效果,使用一次麻煩一點,使用100次就會覺得更加麻煩
- 而使用指令只需要在需要的位置像使用屬性一樣即可;封裝可以麻煩,但是使用越簡單越好
具體實現
封裝準備
1.首先需要一個 js 文件,因為指令實際上就是一個對象,通過在不同的鉤子函數中執(zhí)行對應的邏輯,在本文中,需要使用的鉤子函數是 inserted 和 update,因此可以寫一個基礎的結構,如下:
export default { inserted(el, binding){ }, update(el, binding){ } }
2.然后將這個指令在入口文件 main.js 內進行全局注冊,如下:
import vLoading from '你封裝指令js文件的路徑' // 注冊指令 Vue.directive('jc-loading', vLoading)
3.創(chuàng)建一個 vue 文件來使用這個指令,如下:
<template> <div class="container"> <button style="margin-bottom: 20px" @click="handleClick"> 開關 </button> <div class="box" v-jcLoading="isLoading"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero, temporibus veniam! Totam temporibus ipsam, atque amet aliquid corporis molestiae, perspiciatis asperiores doloremque enim explicabo aperiam. Vel doloremque voluptatibus incidunt quae suscipit cupiditate. Obcaecati sunt, consectetur voluptas sequi aliquam omnis, rem non molestiae assumenda illum quasi excepturi error voluptatibus pariatur nulla. </div> </div> </template> <script> export default { data() { return { isLoading: false } }, methods: { handleClick() { this.isLoading = !this.isLoading } } } </script> <style lang="less" scoped> .container { width: 100vw; height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; .box { width: 500px; height: 300px; padding: 20px; border: 1px solid #999; color: #f40; } } </style>
4.查看一下指令內的輸出語句是否正常執(zhí)行,如圖:
5.正常進行了打印,現在我們進行正式的編寫
實現 loading 效果
1.實現這一步其實也非常的簡單,找一個你覺得好看或者合適的加載效果,按照正常的 html+css+js 進行實現就好,當你實現好之后,需要做的就是使用 js 進行動態(tài)的創(chuàng)建這些元素,所以我們需要有一個函數幫助我們完成這一步,如下:
// 導入模塊化的 less 文件 import styles from './loading.module.less' // 創(chuàng)建 loading 元素 function createLoading() { // 創(chuàng)建 load 遮罩 const loadingMask = document.createElement('div') loadingMask.dataset.role = 'jc-loading' loadingMask.classList.add(styles['jc-loading-mask']) // 創(chuàng)建 loading 旋轉容器元素 const loadingSpinner = document.createElement('div') loadingSpinner.classList.add(styles['jc-loading-spinner']) loadingMask.appendChild(loadingSpinner) // 創(chuàng)建文本片段 const fragment = document.createDocumentFragment() // 創(chuàng)建子元素進行旋轉縮放 for (let i = 0; i < 12; i++) { const div = document.createElement('div') div.style = `--i:${i}` div.classList.add(styles['jc-loading-spinner__circle']) fragment.appendChild(div) } loadingSpinner.appendChild(fragment) return loadingMask }
2.代碼還是非常簡單的,具體取決于你本身實現的 loading 效果,我這個是一個比較簡單的動效,上面這個地方如果有疑問那應該就是證據導入語句,在 vue 中,如果希望一個 less 文件作為一個模塊導入和使用,需要將文件命名改為 文件名.module.less
這種格式,即文件后綴為 .module.less,我們在 inserted 鉤子函數中打印一下這個導入的 styles,如下:
export default { inserted(el, binding){ console.log(styles) }, update(el, binding){ } }
3.結果如圖:
4.k 為 less 文件中開發(fā)時書寫的類名,而后面的 v 表示實際的類名,本案例中 less 文件代碼如下:
.jc-loading-mask { position: absolute; inset: 0; background-color: rgba(0, 0, 0, 0.7); } .jc-loading-spinner { position: absolute; left: calc(50% - 25px); top: calc(50% - 25px); width: 50px; height: 50px; animation: sp 4s linear infinite; } .jc-loading-spinner__circle { position: absolute; top: 0; left: calc(50% - 3px); width: 6px; height: 6px; transform: rotate(calc(var(--i) * (360deg / 12))); transform-origin: center 25px; } .jc-loading-spinner__circle::before { content: ''; inset: 0; border-radius: 50%; position: absolute; background-color: #ff6348; animation: zoom 2.5s linear infinite; animation-delay: calc(var(--i) * 0.2s); } @keyframes sp { to { transform: rotate(360deg); } } @keyframes zoom { 0% { transform: scale(1.2); } 50% { transform: scale(0.5); } 100% { transform: scale(1.2); } }
5.這些 css 樣式我就不再贅述了,先不進行其他邏輯判斷,只展示到頁面上,看看效果,代碼如下:
export default { inserted(el, binding){ el.appendChild(createLoading()) }, update(el, binding){ } }
6.效果如圖:
7.其實也不難對吧,這個效果你可以根據自己的需求來進行更換,但是實現方法都是可以套用的
loading 顯示與隱藏
1.把這個需求梳理清楚之后,后面的就呼之欲出了,什么時候顯示,必然是指令上的值為 true 的時候,隱藏則相反,這是一個先決條件
2.在這個條件之后呢?還需要考慮什么呢?是不是需要創(chuàng)建這個 loading 效果的元素啊,當指令的值為 true 且不存在當前的 loading 元素的時候,才需要創(chuàng)建,而指令的值為 false ,則是當前的 loading 元素存在的話,就需要移除啊
3.基于上面的條件,我們需要一個輔助函數,來幫助我們查找當前 loading 效果的元素是否存在,如下:
function getLoading(container) { return container.querySelector(`[data-role="jc-loading"]`) }
4.所以我們在 inserted 鉤子函數中,應該進行判斷,當指令的值為 true 且元素不存在時,就創(chuàng)建元素并添加,如下:
inserted(el, binding){ // 如果為 true 且不存在加載元素就創(chuàng)建元素添加加載效果 if (!getLoading(el)) { const loading = createLoading() el.appendChild(loading) } }
5.而 update 函數中的代碼是不是也可以寫出來了,進行條件判斷來執(zhí)行邏輯,而且不難發(fā)現其實這個條件與 inserted 中的條件是重合的,所以我們可以封裝為一個函數,如下:
// 開啟加載效果 function openLoading(el, binding) { // 如果為 false 且存在加載元素就移除加載元素 if (!binding.value) { const dom = getLoading(el) dom && dom.remove() } else { // 如果為 true 且不存在加載元素就創(chuàng)建元素添加加載效果 if (!getLoading(el)) { const loading = createLoading() el.appendChild(loading) } } }
6.當然,還需要考慮當前顯示加載元素的 dom 是不是存在相對定位,如果不存在則改為相對定位,最后指令對象的實際代碼如下:
export default { inserted(el, binding){ // 檢測綁定的元素的 position 屬性是否為 static if (window.getComputedStyle(el).position === 'static') { // 如果是則改為相對定位 el.style.position = 'relative' } openLoading(el, binding) }, update(el, binding){ openLoading(el, binding) } }
7.我們看一下實際的效果,如圖:
使用修飾符擴展
1.通過 modifiers(修飾符) 進行一個擴展,當指令了添加了修飾符 body 的時候,loading 就會插入到 body 里面,填充 body,所以我們還需要進行一些額外的判斷,如下:
function getContainer(el, binding) { return binding.modifiers.body ? document.body : el } export default { inserted(el, binding) { if (window.getComputedStyle(el).position === 'static') { el.style.position = 'relative' } openLoading(getContainer(el, binding), binding) }, update(el, binding) { openLoading(getContainer(el, binding), binding) } }
2.此時在組件中使用添加修飾符 body 即可,如下:
<!-- 添加修飾符.body --> <div class="box" v-jcLoading.body="isLoading"> ... </div>
3.查看效果,如圖:
4.元素實際插入的位置,如圖:
完整代碼與結語
1.現在已經具備了一個 loading 指令基本的效果,如果還需要進行其他擴展,比如傳遞給 loading 指令的值不是一個單純的布爾值,而是一個對象,如下:
{ loading:true, color: 'blue', text: '拼命加載中...' ... }
2.通過這些配置來增強指令的效果,有興趣的可以自己試試
3.完整指令代碼:
import styles from './loading.module.less' function getLoading(container) { return container.querySelector(`[data-role="jc-loading"]`) } function createLoading() { const loadingMask = document.createElement('div') loadingMask.dataset.role = 'jc-loading' loadingMask.classList.add(styles['jc-loading-mask']) const loadingSpinner = document.createElement('div') loadingSpinner.classList.add(styles['jc-loading-spinner']) loadingMask.appendChild(loadingSpinner) const fragment = document.createDocumentFragment() for (let i = 0; i < 12; i++) { const div = document.createElement('div') div.style = `--i:${i}` div.classList.add(styles['jc-loading-spinner__circle']) fragment.appendChild(div) } loadingSpinner.appendChild(fragment) return loadingMask } function openLoading(el, binding) { if (!binding.value) { const dom = getLoading(el) dom && dom.remove() } else { if (!getLoading(el)) { const loading = createLoading() el.appendChild(loading) } } } function getContainer(el, binding) { return binding.modifiers.body ? document.body : el } export default { inserted(el, binding) { if (window.getComputedStyle(el).position === 'static') { el.style.position = 'relative' } openLoading(getContainer(el, binding), binding) }, update(el, binding) { openLoading(getContainer(el, binding), binding) } }
4.less 樣式代碼:
.jc-loading-mask { position: absolute; inset: 0; background-color: rgba(0, 0, 0, 0.7); } .jc-loading-spinner { position: absolute; left: calc(50% - 25px); top: calc(50% - 25px); width: 50px; height: 50px; animation: sp 4s linear infinite; } .jc-loading-spinner__circle { position: absolute; top: 0; left: calc(50% - 3px); width: 6px; height: 6px; transform: rotate(calc(var(--i) * (360deg / 12))); transform-origin: center 25px; } .jc-loading-spinner__circle::before { content: ''; inset: 0; border-radius: 50%; position: absolute; background-color: #ff6348; animation: zoom 2.5s linear infinite; animation-delay: calc(var(--i) * 0.2s); } @keyframes sp { to { transform: rotate(360deg); } } @keyframes zoom { 0% { transform: scale(1.2); } 50% { transform: scale(0.5); } 100% { transform: scale(1.2); } }
到此這篇關于如何實現vue加載指令 v-loading的文章就介紹到這了,更多相關vue加載指令 v-loading內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue+elementUi中的table實現跨頁多選功能(示例詳解)
最近在開發(fā)工業(yè)品超市的后臺系統(tǒng),遇到一個需求,就是實現在一個table表格中多選數據,在網上查了好多,有些方法真的是無語,下面通過本文給大家分享vue+elementUi中的table實現跨頁多選功能,感興趣的朋友跟隨小編一起看看吧2024-05-05