v-loading指令实现原理

3,331 阅读1分钟

1.主要思路

这里讲的是v-loading的区域加载

  • 获得使用v-loading指令的dom(使用targetDom指代);
  • loading组件插入到targetDom
  • 获取targetDom的宽高定位等样式,并赋值给loading组件样式,保证loading组件在区域内全覆盖
  • 需要注意的是:loading组件需要脱离文档流,如果targetDom没有定位属性,需要补充上position: relative, 并且loading组件需要决对定位position: absolute

2.文件结构

@/components/VLoading
├─ index.js
└─ v-loading.vue

3.组件使用

3-1.组件注册

//在项目的main.js文件中注册组件
import VLoading from "@/components/VLoading/index.js"
...
Vue.use(VLoading)

3-2.组件使用

<template>
    <div v-loading="loading">this is a test</div>
</template>
<script>
export default{
    data(){
        return {
            loading: true
        }
    },
    Mounted(){
        window.setTimeout(()=> { this.loading = false })
    }
    
}
</script>

4.组件代码

4-1. v-loading.vue

<template>
    <div v-show="visible">加载中...</div>
</template>
<script>
export default{
    data(){
        return {
            visible: true
        }
    }
}
</script>
<style lang="scss" scoped>
div{
    position: absolute;
    top: 0;
    left:0;
    width: 100%;
    height: 100%;
    z:index: 9999
}
</style>

4-2. index.js

import loadingComponent from "./v-loading.vue"

const renderComponent = (component) => {
    //接受组件实例, 返回组件的真实Dom树和组件实例
     const Constructor = Vue.extend(component)
     const instance = new Constructor()
     const dom = instance.$mount().$el
     return { instance, dom }
}

export default  {
    install(Vue){
        const { instance, dom } = renderComponent(loadingComponent)
        Vue.directive("loading", {
            bind(el, binding) {
                Vue.nextTick(()=>{
                    //获取绑定指令的元素的计算样式
                    const style = window.getComputedStyle(el,null)
                    if(style.position === "static") el.style.position = "relative"
                    el.appendChild(dom)
                    instance.visible = Boolean(binding.value)
                    el.instance = instance
                })
            },
            update(el, binding){
                el.instance.visible = Boolean(binding.value)
            }
        })
    }
}

5. 补充说明

5-1.关于vue插件的问题

官方文档: Vue-plugins

import VLoading from "@/components/VLoading/index.js" 

Vue.use(VLoading)
//等价于
if(isFunction(Vloading)){
    VLoading(Vue)
}else if(isFunction(Vloading?.install)){
    VLoading.install(Vue)
}
//所以VLoading需要导出带有install方法的对象,或者一个insatll函数

5-2.关于组件实例化的问题

官方文档: Vue-extend

官方文档: vm-mount

const renderComponent = (component) => {
    // 使用Vue.extend生成组件构造器
    const Constructor = Vue.extend(component)
    // 生成一个组件实例
    const instance = new Constructor()
    // 将组件挂载后拿到真实dom
    const dom = instance.$mount().$el
    ...
}