Vue3實(shí)現(xiàn)全局loading指令的示例詳解
前言
- 公司在開發(fā)組件庫的時(shí)候,我在封裝
loading這個(gè)組件時(shí),需要?個(gè)全局使?的loading,?插件也可以搞定,但是項(xiàng)?開發(fā)周期不太緊張,所以決定??手寫?個(gè)。 - 寫?個(gè)全局的
componnets也?,但是需要在每個(gè)使?的頁?進(jìn)?導(dǎo)?并且注冊(cè),因?yàn)?loading?到的地?會(huì)很多,所以我覺得需要用更優(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.新建?個(gè)loading.js?件,?來寫loading的?定義指令邏輯
const loadingDirective = {
mounted(el,binding) {
},
updated(el,binding) {
},
}
return loadingDirective // 導(dǎo)出在創(chuàng)建的 loadingDirective 對(duì)象中,寫?兩個(gè)鉤?函數(shù),因?yàn)槲覀兿M谶@兩個(gè)生命周期函數(shù),對(duì)它進(jìn)?操作。
2.1 創(chuàng)建這個(gè)組件對(duì)應(yīng)的 DOM
- 新建?個(gè)新的vue實(shí)例,再對(duì)它進(jìn)??個(gè)動(dòng)態(tài)的掛載,掛載之后我們就可以拿到這個(gè)
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對(duì)象跟組件為我們寫好的 loading 組件
const app = createApp(Loading)
// 動(dòng)態(tài)創(chuàng)建?個(gè)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 文件中新增兩個(gè)方法,分別是插入節(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對(duì)象跟組件為我們寫好的 loading 組件
const app = createApp(Loading)
// 動(dòng)態(tài)創(chuàng)建?個(gè)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傳過來的值儲(chǔ)存在 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)插?動(dòng)態(tài)創(chuàng)建的 div 節(jié)點(diǎn),內(nèi)容就是我們的 loading 組件
el.appendChild(el.instance.$el)
}
// 移除節(jié)點(diǎn)
function remove(el) {
// 移除動(dòng)態(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 解決存在的三個(gè)問題
- 位置定位的問題,如果所在掛載的
DOM元素有定位屬性,那么沒有問題,但是所在掛載的DOM元素沒有定位的時(shí)候,那么它??的絕對(duì)定位,就有可能出現(xiàn)問題。 - 頁面滾動(dòng)的問題,如果出現(xiàn)
loading效果, 遮罩層后面的頁面會(huì)發(fā)生滾動(dòng),滾動(dòng)遮罩層時(shí)會(huì)造成底部頁面跟著一塊滾動(dòng)。 - 加載文字需求,我們還可以加入?段?字進(jìn)?展?,不同的地?需要等待時(shí)的?字會(huì)有不同。
2.4.1 ?先來解決第?個(gè)和第二個(gè)問題
- 在插?節(jié)點(diǎn)的時(shí)候去判斷掛載的節(jié)點(diǎn)有沒有定位,如果沒有則為其添加相對(duì)定位。
- 在插?節(jié)點(diǎn)的時(shí)候?yàn)槠涮砑咏?隱藏滾動(dòng)。
- 在移除節(jié)點(diǎn)的時(shí)候移除類名。
const relative = 'g-relative' // g-relative 全局相對(duì)定位樣式名稱
const hidden = 'g-hidden' // g-hidden 全局禁止/隱藏滾動(dòng)樣式名稱
// 插入節(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)插?動(dòng)態(tài)創(chuàng)建的 div 節(jié)點(diǎn),內(nèi)容就是我們的 loading 組件
el.appendChild(el.instance.$el)
}
// 移除節(jié)點(diǎn)
function remove(el) {
// 移除動(dòng)態(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 最后解決第三個(gè)問題,動(dòng)態(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對(duì)象跟組件為我們寫好的 loading 組件
const app = createApp(Loading)
// 動(dòng)態(tài)創(chuàng)建?個(gè)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傳過來的值儲(chǔ)存在 binding.value 中
if (binding.value) {
append(el)
}
// 在此判斷是否有title值
if (binding.arg !== 'undefined') {
// setTitle 使我們?cè)趌oading組件中定義的?法
el.instance.setTitle(binding.arg)
}
},
updated (el, binding) {
// 在此判斷是否有title值
if (binding.arg !== 'undefined') {
// setTitle 使我們?cè)趌oading組件中定義的?法
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 全局相對(duì)定位樣式名稱
const hidden = 'g-hidden' // g-hidden 全局禁止/隱藏滾動(dòng)樣式名稱
// 插入節(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)插?動(dòng)態(tài)創(chuàng)建的 div 節(jié)點(diǎn),內(nèi)容就是我們的 loading 組件
el.appendChild(el.instance.$el)
}
// 移除節(jié)點(diǎn)
function remove(el) {
// 移除動(dòng)態(tài)創(chuàng)建的 div 節(jié)點(diǎn)
el.removeChild(el.instance.$el)
el.classList.remove(relative) // 移除類名
el.classList.remove(hidden) // 移除類名
}3.在main.js文件中引?注冊(cè)
想要在全局使?這個(gè)自定義指令,那么我們需要在
main.js文件中去引?注冊(cè)。
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) // 全局注冊(cè)loading指令
.mount('#app')4.在頁面中使用自定義loading指令
在
onMounted的時(shí)候,判斷loading是否顯?,在做判斷之前,先來看看我們?cè)趯懞?loading?定義指令之后,該如何在頁面中使用它。
<template>
<div v-loading="loading"></div> // 只需在節(jié)點(diǎn)上寫上 v-loading='loading' 即可,后邊的loading是?個(gè)值
</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í)我們?cè)?loading 組件中就可以接收到傳?的值,我們根據(jù)值來判斷是否顯? loading 組件
到此這篇關(guān)于Vue3實(shí)現(xiàn)全局loading指令的文章就介紹到這了,更多相關(guān)Vue3 全局loading指令內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- vue全局注冊(cè)自定義指令防抖解析
- vue全局自定義指令和局部自定義指令的使用
- Vue全局自定義指令Modal拖拽的實(shí)踐
- vue全局自定義指令-元素拖拽的實(shí)現(xiàn)代碼
- vue directive定義全局和局部指令及指令簡寫
- 對(duì)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à)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
前端Vue3引入高德地圖并展示行駛軌跡動(dòng)畫的步驟
最近在Vue項(xiàng)目中引入高德地圖,實(shí)現(xiàn)地圖展示與交互的方法和技術(shù),這里跟大家分享下,這篇文章主要給大家介紹了關(guān)于前端Vue3引入高德地圖并展示行駛軌跡動(dòng)畫的相關(guān)資料,需要的朋友可以參考下2024-09-09
Vue?3?表格時(shí)間監(jiān)控與動(dòng)態(tài)后端請(qǐng)求觸發(fā)詳解?附Demo展示
在Vue3中,使用el-table組件渲染表格數(shù)據(jù),通過el-table-column指定內(nèi)容,時(shí)間點(diǎn)需前端校準(zhǔn),用getTime()比較,到達(dá)時(shí)觸發(fā)操作,異步API請(qǐng)求可用async/await處理,setInterval實(shí)現(xiàn)定時(shí)監(jiān)控,配合條件判斷防止重復(fù)請(qǐng)求2024-09-09
Vue3.x如何設(shè)置瀏覽器動(dòng)態(tài)Title方法
這篇文章主要介紹了Vue3.x如何設(shè)置瀏覽器動(dòng)態(tài)Title方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
vue: WebStorm設(shè)置快速編譯運(yùn)行的方法
今天小編就為大家分享一篇vue: WebStorm設(shè)置快速編譯運(yùn)行的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-10-10
vue2.0+vue-router構(gòu)建一個(gè)簡單的列表頁的示例代碼
這篇文章主要介紹了vue2.0+vue-router構(gòu)建一個(gè)簡單的列表頁的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02

