Vue3實(shí)現(xiàn)全局loading指令的示例詳解
前言
- 公司在開發(fā)組件庫的時(shí)候,我在封裝
loading
這個組件時(shí),需要?個全局使?的loading
,?插件也可以搞定,但是項(xiàng)?開發(fā)周期不太緊張,所以決定??手寫?個。 - 寫?個全局的
componnets
也?,但是需要在每個使?的頁?進(jìn)?導(dǎo)?并且注冊,因?yàn)?loading
?到的地?會很多,所以我覺得需要用更優(yōu)雅的方式來實(shí)現(xiàn)它。
1.完成loading組件
<template> <div class="mask"> <div class="loader"> <div> <div class="dot"></div> <div class="dot"></div> <div class="dot"></div> <div class="dot"></div> <div class="dot"></div> </div> <div class="tip-text">{{ title }}</div> </div> </div> </template> <script setup> import { ref, defineExpose } from 'vue' let title = ref('') // 加載提示文字 // 更改加載提示文字 const setTitle = (val) => { title.value = val } // 暴露出去 defineExpose({ title, setTitle }) } </script> <style lang="less" scoped> .mask { width: 100%; height: 100%; position: absolute; top: 0; left: 0; background-color: rgba(0, 0, 0, 0.8); z-index: 99; .loader { position: absolute; top: 50%; left: 40%; margin-left: 10%; transform: translate3d(-50%, -50%, 0); display: flex; flex-direction: column; align-items: center; } .tip-text { color: #3793ff; padding-top: 10px; } .dot { width: 18px; height: 18px; border-radius: 100%; display: inline-block; animation: slide 1s infinite; } .dot:nth-child(1) { animation-delay: 0.1s; background: #1fbfff; } .dot:nth-child(2) { animation-delay: 0.2s; background: #2ea4ff; } .dot:nth-child(3) { animation-delay: 0.3s; background: #3793ff; } .dot:nth-child(4) { animation-delay: 0.4s; background: #3b89ff; } .dot:nth-child(5) { animation-delay: 0.5s; background: #4577ff; } } @-moz-keyframes slide { 0% { transform: scale(1); } 50% { opacity: 0.3; transform: scale(2); } 100% { transform: scale(1); } } @-webkit-keyframes slide { 0% { transform: scale(1); } 50% { opacity: 0.3; transform: scale(2); } 100% { transform: scale(1); } } @-o-keyframes slide { 0% { transform: scale(1); } 50% { opacity: 0.3; transform: scale(2); } 100% { transform: scale(1); } } @keyframes slide { 0% { transform: scale(1); } 50% { opacity: 0.3; transform: scale(2); } 100% { transform: scale(1); } } </style>
2.新建?個loading.js?件,?來寫loading的?定義指令邏輯
const loadingDirective = { mounted(el,binding) { }, updated(el,binding) { }, } return loadingDirective // 導(dǎo)出
在創(chuàng)建的 loadingDirective
對象中,寫?兩個鉤?函數(shù),因?yàn)槲覀兿M谶@兩個生命周期函數(shù),對它進(jìn)?操作。
2.1 創(chuàng)建這個組件對應(yīng)的 DOM
- 新建?個新的vue實(shí)例,再對它進(jìn)??個動態(tài)的掛載,掛載之后我們就可以拿到這個
DOM
的實(shí)例了
具體實(shí)現(xiàn)看如下代碼塊
import { createApp } from 'vue' // 導(dǎo)? createApp ?法 import Loading from './loading' // 導(dǎo)?我們寫好的 loading 組件 const loadingDirective = { mounted(el, binding) { // 創(chuàng)建app對象跟組件為我們寫好的 loading 組件 const app = createApp(Loading) // 動態(tài)創(chuàng)建?個div節(jié)點(diǎn),將app掛載在div上 // 我們的 loading 組件將替換此 div 標(biāo)簽的 innerHTML const instance = app.mount(document.createElement('div')) }, updated(el, binding) {}, } return loadingDirective // 導(dǎo)出
2.2 在 loading.js 文件中新增兩個方法,分別是插入節(jié)點(diǎn)和移除節(jié)點(diǎn)。
import { createApp } from 'vue' // 導(dǎo)? createApp ?法 import Loading from './loading' // 導(dǎo)?我們寫好的 loading 組件 const loadingDirective = { mounted(el, binding) { // 創(chuàng)建app對象跟組件為我們寫好的 loading 組件 const app = createApp(Loading) // 動態(tài)創(chuàng)建?個div節(jié)點(diǎn),將app掛載在div上 // 我們的 loading 組件將替換此 div 標(biāo)簽的 innerHTML const instance = app.mount(document.createElement('div')) // 因?yàn)樵趗pdated也需要?到 instance 所以將 instance 添加在 el 上 // 在 updated中 通過el.instance 可訪問到 el.instance = instance // v-loading傳過來的值儲存在 binding.value 中 if (binding.value) { append(el) } }, updated(el, binding) { remove(el) }, } return loadingDirective // 導(dǎo)出 // 插入節(jié)點(diǎn) function append(el) { // 向el節(jié)點(diǎn)插?動態(tài)創(chuàng)建的 div 節(jié)點(diǎn),內(nèi)容就是我們的 loading 組件 el.appendChild(el.instance.$el) } // 移除節(jié)點(diǎn) function remove(el) { // 移除動態(tài)創(chuàng)建的 div 節(jié)點(diǎn) el.removeChild(el.instance.$el) }
2.3 完善 updated 周期函數(shù)
在?開始的時(shí)候?qū)⒐?jié)點(diǎn)插?,在 v-loading 的值發(fā)?改變時(shí)候觸發(fā) updated 我們將節(jié)點(diǎn)移除, 但是這樣寫是不合適的,我們需要完善?下在 updated 中的操作。
updated (el, binding) { // 如果value的值有改變,那么我們?nèi)ヅ袛噙M(jìn)?操作 if (binding.value !== binding.oldValue) { // 三元表達(dá)式 true 插入 false 移除 binding.value ? append(el) : remove(el) }
基本的指令已經(jīng)完成的差不多了。
2.4 解決存在的三個問題
- 位置定位的問題,如果所在掛載的
DOM
元素有定位屬性,那么沒有問題,但是所在掛載的DOM
元素沒有定位的時(shí)候,那么它??的絕對定位,就有可能出現(xiàn)問題。 - 頁面滾動的問題,如果出現(xiàn)
loading
效果, 遮罩層后面的頁面會發(fā)生滾動,滾動遮罩層時(shí)會造成底部頁面跟著一塊滾動。 - 加載文字需求,我們還可以加入?段?字進(jìn)?展?,不同的地?需要等待時(shí)的?字會有不同。
2.4.1 ?先來解決第?個和第二個問題
- 在插?節(jié)點(diǎn)的時(shí)候去判斷掛載的節(jié)點(diǎn)有沒有定位,如果沒有則為其添加相對定位。
- 在插?節(jié)點(diǎn)的時(shí)候?yàn)槠涮砑咏?隱藏滾動。
- 在移除節(jié)點(diǎn)的時(shí)候移除類名。
const relative = 'g-relative' // g-relative 全局相對定位樣式名稱 const hidden = 'g-hidden' // g-hidden 全局禁止/隱藏滾動樣式名稱 // 插入節(jié)點(diǎn) function append(el) { const style = getComputedStyle(el) el.classList.add(hidden) // 添加類名 if (['absolute', 'relative', 'fixed'].indexOf(style.position) === -1) { el.classList.add(relative) // 添加類名 } // 向el節(jié)點(diǎn)插?動態(tài)創(chuàng)建的 div 節(jié)點(diǎn),內(nèi)容就是我們的 loading 組件 el.appendChild(el.instance.$el) } // 移除節(jié)點(diǎn) function remove(el) { // 移除動態(tài)創(chuàng)建的 div 節(jié)點(diǎn) el.removeChild(el.instance.$el) el.classList.remove(relative) // 移除類名 el.classList.remove(hidden) // 移除類名 }
g-relative
g-hidden
是我在全局css?件中寫的樣式
.g-relative { position: relative; } .g-hidden { overflow: hidden; }
2.4.2 最后解決第三個問題,動態(tài)顯示加載文字。
在?的地?使?
:[]
的語法
<template> <div class="demo" v-loading:[title]="loading"></div> </template> <script setup> import { ref } from 'vue' const title = ref('拼命加載中...') const loading = ref(true) } </script>
- 傳?
title
之后需要進(jìn)?接收,并插?title
在loading
中 - 通過
binding.arg
可接收到:[]
傳來的值
import { createApp } from 'vue' // 導(dǎo)? createApp ?法 import Loading from './loading' // 導(dǎo)?我們寫好的 loading 組件 const loadingDirective = { mounted(el, binding) { // 創(chuàng)建app對象跟組件為我們寫好的 loading 組件 const app = createApp(Loading) // 動態(tài)創(chuàng)建?個div節(jié)點(diǎn),將app掛載在div上 // 我們的 loading 組件將替換此 div 標(biāo)簽的 innerHTML const instance = app.mount(document.createElement('div')) // 因?yàn)樵趗pdated也需要?到 instance 所以將 instance 添加在 el 上 // 在 updated中 通過el.instance 可訪問到 el.instance = instance // v-loading傳過來的值儲存在 binding.value 中 if (binding.value) { append(el) } // 在此判斷是否有title值 if (binding.arg !== 'undefined') { // setTitle 使我們在loading組件中定義的?法 el.instance.setTitle(binding.arg) } }, updated (el, binding) { // 在此判斷是否有title值 if (binding.arg !== 'undefined') { // setTitle 使我們在loading組件中定義的?法 el.instance.setTitle(binding.arg) } // 如果value的值有改變,那么我們?nèi)ヅ袛噙M(jìn)?操作 if (binding.value !== binding.oldValue) { // 三元表達(dá)式 true 插入 false 移除 binding.value ? append(el) : remove(el) } } } return loadingDirective // 導(dǎo)出 const relative = 'g-relative' // g-relative 全局相對定位樣式名稱 const hidden = 'g-hidden' // g-hidden 全局禁止/隱藏滾動樣式名稱 // 插入節(jié)點(diǎn) function append(el) { const style = getComputedStyle(el) el.classList.add(hidden) // 添加類名 if (['absolute', 'relative', 'fixed'].indexOf(style.position) === -1) { el.classList.add(relative) // 添加類名 } // 向el節(jié)點(diǎn)插?動態(tài)創(chuàng)建的 div 節(jié)點(diǎn),內(nèi)容就是我們的 loading 組件 el.appendChild(el.instance.$el) } // 移除節(jié)點(diǎn) function remove(el) { // 移除動態(tài)創(chuàng)建的 div 節(jié)點(diǎn) el.removeChild(el.instance.$el) el.classList.remove(relative) // 移除類名 el.classList.remove(hidden) // 移除類名 }
3.在main.js文件中引?注冊
想要在全局使?這個自定義指令,那么我們需要在
main.js
文件中去引?注冊。
import { createApp } from 'vue' import App from './App.vue' import '@/assets/scss/global.css' // 引入全局樣式文件 import loadingDirective from '@/views/loading/directive' //引?loading?定義指令 createApp(App) .directive('loading', loadingDirective) // 全局注冊loading指令 .mount('#app')
4.在頁面中使用自定義loading指令
在
onMounted
的時(shí)候,判斷loading
是否顯?,在做判斷之前,先來看看我們在寫好loading
?定義指令之后,該如何在頁面中使用它。
<template> <div v-loading="loading"></div> // 只需在節(jié)點(diǎn)上寫上 v-loading='loading' 即可,后邊的loading是?個值 </template> <script setup> import { ref, onMounted } from 'vue' const loading = ref(true) // 默認(rèn)讓 loading 值為true 傳給loading組件 onMounted(() => { // 定時(shí)器模擬數(shù)據(jù)加載完成之后更改 loading 狀態(tài) setTimenout(() => { loading.value = false }, 3000) }) </script>
此時(shí)我們在 loading 組件中就可以接收到傳?的值,我們根據(jù)值來判斷是否顯? loading 組件
到此這篇關(guān)于Vue3實(shí)現(xiàn)全局loading指令的文章就介紹到這了,更多相關(guān)Vue3 全局loading指令內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- vue全局注冊自定義指令防抖解析
- vue全局自定義指令和局部自定義指令的使用
- Vue全局自定義指令Modal拖拽的實(shí)踐
- vue全局自定義指令-元素拖拽的實(shí)現(xiàn)代碼
- vue directive定義全局和局部指令及指令簡寫
- 對Vue2 自定義全局指令Vue.directive和指令的生命周期介紹
- vue3的自定義指令directives實(shí)現(xiàn)
- vue 自定義指令directives及其常用鉤子函數(shù)說明
- vue?filters和directives訪問this的問題詳解
- vue通過指令(directives)實(shí)現(xiàn)點(diǎn)擊空白處收起下拉框
- 詳解vue + vuex + directives實(shí)現(xiàn)權(quán)限按鈕的思路
- vue全局指令文件 directives詳解
相關(guān)文章
Vue中的echarts圖表如何實(shí)現(xiàn)loading效果
這篇文章主要介紹了Vue中的echarts圖表如何實(shí)現(xiàn)loading效果,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08Vue?3?表格時(shí)間監(jiān)控與動態(tài)后端請求觸發(fā)詳解?附Demo展示
在Vue3中,使用el-table組件渲染表格數(shù)據(jù),通過el-table-column指定內(nèi)容,時(shí)間點(diǎn)需前端校準(zhǔn),用getTime()比較,到達(dá)時(shí)觸發(fā)操作,異步API請求可用async/await處理,setInterval實(shí)現(xiàn)定時(shí)監(jiān)控,配合條件判斷防止重復(fù)請求2024-09-09Vue3.x如何設(shè)置瀏覽器動態(tài)Title方法
這篇文章主要介紹了Vue3.x如何設(shè)置瀏覽器動態(tài)Title方法,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03vue: WebStorm設(shè)置快速編譯運(yùn)行的方法
今天小編就為大家分享一篇vue: WebStorm設(shè)置快速編譯運(yùn)行的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10vue2.0+vue-router構(gòu)建一個簡單的列表頁的示例代碼
這篇文章主要介紹了vue2.0+vue-router構(gòu)建一個簡單的列表頁的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02