当前位置:   article > 正文

vue3进阶用法之通过调用函数动态加载组件用法及示例

vue3进阶用法之通过调用函数动态加载组件用法及示例

业务场景

假设现在有一个可能在全局任何地方调用的vue组件你会怎么办?非常简单,在app.vue下的router-view同级写上这个组件,在全局中加一个变量v-if判断这个变量就解决了!

tempalte中
<div>
	<router-view />
	<YourComponent v-if="xxxStore.yourComponentVisible" />
</div>
  • 1
  • 2
  • 3
  • 4
js中
export const showComponent = () => {
	xxxStore.yourComponentVisible = true;
};
export const hideComponent = () => {
	xxxStore.yourComponentVisible = false;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
某页面中
const handleSubmitForm = async() = >{
	showComponent()
	await reqGetUserInfo()
     hideComponent()
}
  • 1
  • 2
  • 3
  • 4
  • 5

完美!!本文结束!!

一点也不好笑,我们进入正题。先看看需要了解到的方法,再来实现这个功能。

主要方法

  • h函数(更准确的名字应该叫createVnode)

从字面意思上来看,简单来说这个东西就是创建虚拟dom用的,在本文里面,它可以将你写的组件,转成vnode,也就是虚拟Dom。这里不做详细赘述。可以看下面文档链接。
api详细用法及信息

  • render函数

白话说就是把你的虚拟dom通过该方法挂载到某一个节点上面。这里不做详细赘述。可以看下面文档链接

api详细用法及信息

实现思路

看过了上面两个方法,先把组件转成虚拟dom,再将虚拟dom挂载到某节点上。那么知道了这些,实现这个功能就很简单了。这里用一个全局loading组件做示例。

简单版实现代码

import UploadLoading from '@/components/UploadLoading/index.vue'
import { render, h, ref, RendererNode, RendererElement, VNode, } from 'vue';
interface ILoadingRef {
    vnode: VNode<RendererNode, RendererElement, {
        [key: string]: any;
    }>;
    div: HTMLDivElement;
}
/**
 * 任何页面中调用该组件进行展示
 * @returns 
 */
export const useUploadLoading = () => {
    const loadingRef = ref<ILoadingRef | null>(null);
    const showLoading = () => {
        // 避免重复挂载组件
        if (loadingRef.value) return
        // 在body上挂载一个div
        const div = document.createElement('div');
        document.body.appendChild(div);
        // 把组件转成虚拟dom
        const vnode = h(UploadLoading);
        // 把这个组件挂载到上面创建的div里面
        render(vnode, div);
        loadingRef.value = { vnode, div };
    };

    const hideLoading = () => {
        if (!loadingRef.value) return
        // 在vue中卸载(unMounted)这个元素
        render(null, loadingRef.value.div);
        // Vue 卸载了这个元素,它仍然存在于 DOM 中,需要手动移除
        document.body.removeChild(loadingRef.value.div);
        loadingRef.value = null;
    };

    return { showLoading, hideLoading };
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

那么问题来了,为什么要走下面两个方法呢?这不是一个意思吗?

render(null, loadingRef.value.div);
document.body.removeChild(loadingRef.value.div);
  • 1
  • 2

其实这里看似一样其实另有玄机,render函数让这个组件执行所有的组件卸载生命周期,但元素还在dom上。比如说你的组件中的unMounted中有移除某些事件监听器的方法。要先走这一步让组件按正常流程卸载。

document.body.removeChild才是实际移除这个组件的操作

组件拓展

上面只是说简单实现。那么,现在这个loading组件中,有一个进度条,我需要实时更新这个进度条的进度,怎么办?

那就需要用到h函数的第二个参数了,我们把这个进度条的值传到组件里面。

组件中的props:

const props = defineProps({
	progress: {
		type: Number,
		default: 0
	}
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

核心两行代码:

   const vnode = h(UploadLoading, { progress: progress.value });
   loadingRef.value.vnode.component.props.progress = newProgress;
  • 1
  • 2

完整代码:

import UploadLoading from '@/components/UploadLoading/index.vue'
import { render, h, ref, RendererNode, RendererElement, VNode } from 'vue';
interface ILoadingRef {
    vnode: VNode<RendererNode, RendererElement, {
        [key: string]: any;
    }>;
    div: HTMLDivElement;
}
/**
 * 任何页面中调用该组件进行展示
 * @returns 
 */
export const useUploadLoading = () => {
    const loadingRef = ref<ILoadingRef | null>(null);
    const progress = ref(0);
    const showLoading = () => {
        if (!loadingRef.value) {
            const div = document.createElement('div');
            document.body.appendChild(div);
            // 这里第二个参数传组件的props参数
            const vnode = h(UploadLoading, { progress: progress.value });
            render(vnode, div);
            loadingRef.value = { vnode, div };
        }
    };

    const hideLoading = () => {
        if (loadingRef.value) {
            render(null, loadingRef.value.div);
            document.body.removeChild(loadingRef.value.div);
            loadingRef.value = null;
            // 重置进度条
            progress.value = 0
        }
    };
    const updateProgress = (newProgress: number) => {
        progress.value = newProgress;
        if (loadingRef.value && loadingRef.value.vnode.component) {
            // 修改组件中的进度条值
            loadingRef.value.vnode.component.props.progress = newProgress;
        }
    };
    return { showLoading, hideLoading, updateProgress };
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

最后直接引入使用就行,我这里用oss文件分片上传做示例:

  /**
     * 分片上传文件
     */
    async uploadFileByChunk(options: IChunkUploadOptions): Promise<string> {
        const { showLoading, hideLoading, updateProgress } = useUploadLoading();
        const yourOptions = {
            // 获取分片上传进度、断点和返回值。
            progress: (p: number) => {
                updateProgress(Math.floor(p * 100))
            }
            ...省略其他代码
        }
        showLoading()
        try {
            await ossUpload(yourOptions)
            ...省略其他代码
        } finally {
            hideLoading()
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

总结

vue官方文档那边虽然说了函数的使用方法,但没有列举这种业务场景。这里只是做一个简单的实例,其实可以通过这种方法实现其他更多的功能,比如说modal框等等等

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/1000030
推荐阅读
相关标签
  

闽ICP备14008679号