一、在单vue文件中直接使用
1、html部分
<template><divstyle="height: 100%;"v-loading="loading"><ul><li v-for="item in data">{{item}} - {{item * 2}}</li></ul></div>
</template>
2、js部分
<script setup>import {ref} from 'vue'// loading图片路径import imgSrc from '@/views/loading.gif'const data = ref(1)// 定义loading初始值为true,插入loadingconst loading = ref(true)// 单页面自定义指令直接使用v开头驼峰命名变量,如下const vLoading = {// dom加载完,在使用v-loading指令的html节点里添加dommounted (el, binding) {const div = document.createElement('div')div.className = 'loading'const img = document.createElement('img')img.src = imgSrcimg.width = 40div.appendChild(img)el.appendChild(div)},updated(el, binding) {// 当值loading绑定的值为false的时候删除domif (!binding.value) {const loadingDom = document.querySelector('.loading')el.removeChild(loadingDom)}}}// 模拟异步数据,2秒后loading值为falsesetTimeout(() => {loading.value = falsedata.value = 50}, 2000)
</script>
3、实现效果
二、全局注册使用
1、html部分
<template><divstyle="height: 100%;"v-loading="loading"><ul><li v-for="item in data">{{item}} - {{item * 2}}</li></ul></div>
</template>
2、js部分
在components下创建loading文件夹,在loading文件夹里创建directive.js
// direcitve.jsimport imgSrc from "@/views/loading.gif";const loadingDirective = {mounted(el, binding) {const div = document.createElement('div')div.className = 'loading'const img = document.createElement('img')img.src = imgSrcimg.width = 40div.appendChild(img)el.appendChild(div)},updated(el, binding) {if (!binding.value) {const loadingDom = document.querySelector('.loading')el.removeChild(loadingDom)}}
}
export default loadingDirective
在main.js中全局注册指令
// 引入loading
import loadingDirective from './components/loading/directive'let app = createApp(App)app.use(ElementPlus).use(router)
app.directive('loading', loadingDirective) // 全局注册
app.mount('#app')
三、使用vue组件文件实现自定义指令
1、在components下创建loading文件夹,在loading文件夹里创建directive.js
2、在loading文件夹里创建loading.vue
3、在loading文件夹里放入一张GIF图(loading.gif)
1、loading.vue文件源码
// loading.vue<template><div class="loading"><div class="loading-content"><img src="./loading.gif" width="24" height="24" alt=""><p class="desc">{{title}}</p></div></div>
</template><script setup>// vue3 写法import { ref, defineExpose } from 'vue'const title = ref('正在加载...')const setTitle = (t) => {title.value = t}// 导出setTitle方法defineExpose({setTitle})// vue2 写法// export default {// name: 'loading',// data() {// return {// title: '正在加载...'// }// },// methods: {// setTitle(title) {// this.title = title// }// }// }
</script><style scoped lang="scss">.loading {position: absolute;top: 50%;left: 50%;transform: translate3d(-50%, -50%, 0);.loading-content {text-align: center;.desc {line-height: 20px;font-size: $font-size-small;color: $color-text-l;}}}
</style>
2、js部分(directive.js)
// directive.js// 引入vue方法createApp
import {createApp} from 'vue'
// 引入添加dom和删除dom的方法
import {addClass, removeClass} from '@/assets/js/dom'const relativeCls = 'g-relative'
const loadingDirective = {mounted (el, binding) {// 创建一个loading的vue实例const app = createApp(Loading)// 挂载loading.vue 到div DOM上const instance = app.mount(document.createElement('div'))// 把instance挂到要用指令的element下el.instance = instanceconst title = binding.arg// 如果传了title就重新设置title的值if (typeof title !== 'undefined') {instance.setTitle(title)}// 指令绑定的值为true把自定义的vue实例下的dom节点$el添加到el下if (binding.value) {append(el)}},// 指令绑定的值更新以后updated (el, binding) {const title = binding.argif (typeof title !== 'undefined') {el.instance.setTitle(title)}if (binding.value !== binding.oldValue) {// 指令绑定的值为true添加指令dom,否则删除指令的dombinding.value ? append(el) : remove(el)}}
}function append(el) {const style = getComputedStyle(el)// 如果要绑定的dom没有定位就添加一个有定位的classiif (!['absolute', 'fixed', 'relative'].includes(style.position)) {addClass(el, relativeCls)}el.appendChild(el.instance.$el)
}function remove(el) {removeClass(el, relativeCls)el.removeChild(el.instance.$el)
}export default loadingDirective
3、@/assets/js/dom.js源码
export function addClass(el, className) {if (!el.classList.contains(className)) {el.classList.add(className)}
}export function removeClass(el, className) {el.classList.remove(className)
}
4、main.js源码
// 引入loading
import loadingDirective from './components/loading/directive'let app = createApp(App)app.use(ElementPlus).use(router)
app.directive('loading', loadingDirective) // 全局注册
app.mount('#app')
四、进阶(有多个自定义指令)
1、封装一个通用的js
把自定义组件的方法拎出来单独弄一个js文件,我习惯放在src/assets/js/create-my-like-directive.js
// create-my-like-directive.jsimport { createApp } from 'vue'
import { addClass, removeClass } from '@/assets/js/dom'const relativeCls = 'g-relative'export default function createMyLikeDirective (Comp) { // 改动的地方,变成可传参的方法return {mounted (el, binding) {const app = createApp(Comp) // 传入变动参数const instance = app.mount(document.createElement('div'))const name = Comp.nameif (!el[name]) {el[name] = {}}// 把实例挂载到dom的name下,防止多个自定义指令互相影响干扰出现bugel[name].instance = instanceconst title = binding.argif (title) {instance.setTitle(title)}if (binding.value) {append(el)}},updated (el, binding) {const title = binding.argconst name = Comp.nameif (title) {el[name].instance.setTitle(title)}if (binding.value !== binding.oldValue) {binding.value ? append(el) : remove(el)}}}function append (el) {const style = getComputedStyle(el)const name = Comp.nameif (!['absolute', 'fixed', 'relative'].includes(style.position)) {addClass(el, relativeCls)}el.appendChild(el[name].instance.$el)}function remove (el) {const name = Comp.nameremoveClass(el, relativeCls)el.removeChild(el[name].instance.$el)}
}
2、directive.js修改
import Loading from './loading.vue'
import createMyLikeDirective from '@/assets/js/create-my-like-directive'
// 如果有不同的自定义好的vue文件,Loading变为别的vue文件即可
const loadingDirective = createMyLikeDirective(Loading)export default loadingDirective
3、main.js修改
// 引入loading
import loadingDirective from './components/loading/directive'// 引入其他的
import AAAA from './components/AAAA/directive'
import BBBB from './components/BBBB/directive'let app = createApp(App)app.use(ElementPlus).use(router)
app.directive('loading', loadingDirective) // 全局注册
app.directive('aaaa', AAAA) // 全局注册
app.directive('bbbb', BBBB) // 全局注册
app.mount('#app')