vue3 使用defineAsyncComponent与component标签实现动态渲染组件思路详解
作者:小何开发 发布时间:2024-05-02 16:32:38
内容有些啰嗦,内容记载了当时遇到了bug以及解决问题的思路。
业务场景简述:
前端做配置化组件,通过url内的唯一标识,请求后端sql 哪取页面配置信息,前端通过配置信息动态渲染查询表单,导出、表格,toolbar以及动态弹窗;该动态渲染组件的功能,就是渲染的toolbar内的按钮,类型为自定义弹窗,弹窗路径为后端配置数据,前端通过点击该按钮,打开指定路径下的弹窗组件。
之前用的vue2的动态挂载组件,也没见vue3那么麻烦,官网上的例子以及网上的所以例子都是前端知道要导哪些组件,然后程序进入就直接导进来了,只是写了逻辑动态切换。我这个不一样,需要挂载的组件是未知的。所以实现起来有点吃力。
一、基础的动态引入组件:
简单的动态引入的意思是,前端知道要引入哪些组件,将多个组件引入到父组件中,但不渲染它,满足一定条件后,才去在某个位置渲染指定的组件。
<template>
<custom-modal ref="custom"></custom-modal>
</template>
<script>
import {
reactive,
ref,
shallowReactive,
onActivated,
defineAsyncComponent,
} from 'vue';
const customModal = defineAsyncComponent(() => import('./modal/CustomModal.vue'));
const custom = ref();
</script>
以上的例子就是通过vue的defineAsyncComponent
实现挂载组件,并赋值给customModal
,模板中可以直接使用<custom-modal>
作为标签使用,也可以将它赋值给component中的is属性,is属性执向一个变量,可通过业务逻辑动态,更改该变量的值,就可以实现多个组件进行来回的渲染了
<template>
<component :is="componentKey" ref="custom"></component>
</template>
import {
reactive,
ref,
shallowReactive,
onActivated,
defineAsyncComponent,
} from 'vue';
const componentKey = ref(null);
const components: any = shallowReactive({});
const customModal = defineAsyncComponent(() => import('./modal/CustomModal.vue'));
componentKey = customModal
二、复杂的引入:不确定到底引入什么组件,组件的路径由后端返回
将以上代码 添加到项目代码中,并不能实现,虽然引入不报错,但是ref一直为undefined,无法调用动态组件内的open函数。
不断尝试了很多次,得出以下结论
1.起初是在按钮的click函数内去挂载自定义组件并调用ref函数的,ref为undefined。
尝试多次不能实现功能(这里是挂载与调用最合适的位置),
2.接着又在初始化配置数据时(查询后端sql),axios的then函数内挂载组件,然后点击按钮的地方调用ref内的函数,ref依旧为null。
3. 接着在最外层,调用初始化时挂载,也就是生命周期函数体内,测试还是一样的结果。
4. 接着发现带有async函数体内挂载组件,也无法完成。
5.单独写个函数,不加async,函数内挂载组件,然后再生命周期外调用该函数,按钮内调用ref内的方法,成功弹窗。这并不是我想要的,因为路径不是固定的,它要等到后端sql放回结果,才能执行。
总结:上面的多次测试,得出以下结论,都不能让动态组件ref对象有值
1、不能在组件的事件函数内挂载,
2、不能在axios的then函数体内挂载
3、不能在带有async声明的函数体内挂载
4、不能在vue的生命周期内挂载
5、只能在最外层挂载实现,这时ref才是个对象。
好在天无绝人之路;脑海里有个思路:
页面初始化时将项目里所有的全局挂载view组件扔到一个object内,使用component组件,is:对应object内指定的组件对象,然后通过后端的数据,这时后端就不用给组件路径了,给个组件名,我从object中找到挂载的组件然后将对象给is。const modules = import.meta.glob('@/views/*/**.vue');
// 获取所有项目路径
mudules为views内所有的vue的相对路径,然后循环它,在循环体内实现挂载,将它存入一个对象内,key为相对路径的项目名称(可以截取以下)。
有了上面的思路,通过反复测试和实现,最终功能实现了。
<template>
<component :is="componentKey" ref="custom"></component>
</template>
<script>
import {
reactive,
ref,
shallowReactive,
onActivated,
defineAsyncComponent,
} from 'vue';
//声明componentkey,用于告诉component当前挂载什么组件,components为一个对象,存放多个不确定的自定义组件。
const componentKey = ref(null);
const components: any = shallowReactive({});
// 组件挂载
const initTableConfig = (gridId, type) => {
queryTableConfig({ gridId }).then(({ data }) => {
if (type === 'main') {
Object.assign(mainConfig, data);
tabsKey.value = -1;
} else {
tabsDetail.value.push(data);
tabsKey.value = tabsDetail.value.length - 1;
}
// 涉及到自定义组件的部分,这里需要提前挂载,在用到时不至于ref为null
XEUtils.objectEach(data.action, (action, key) => {
if (
action.modalCfg &&
action.modalCfg.type === 'CustomModal' &&
action.modalCfg.src
) {
components[action.actionId] = defineAsyncComponent(
() => import(`../../../${action.modalCfg.src}`)
);
//注意:这里的路径后端只能返回相对路径,不能使用@/xxx/xxx.vue ,不能使用src/xxx/xxx.vue,只能./xxx.vue或者../../xxx/xxx.vue。由于并不确定组件在什么位置,避免容易出错的原则,我在前端通过../../../的形式将路径回退到src下,后端只需要从src下配置路径即可,不用考虑那么多了。如后端src的值为src/xxx/xxx/xxx.vue 则在前端合成的路径就为../../../src/xx/xxx/xxx.vue
componentKey.value = components[action.actionId];
// 为什么componentKey.vue在这里赋值,在后面点击窗口后又赋值,这里能不能省略。
//答:这里省略的话,到点击按钮触发时会报错,第一次点击会报错,第二次点击不会报错,窗口正常弹出。可能是因为,组件挂载时并没有引入组件,只在使用时才引入,如果上面不提前将挂载好的组件引入进来,后面触发事件触发时引入在调用ref,执行太快,costom就会报错,所以才会点两次才弹窗。
}
});
});
};
</script>
按钮点击触发事件,确定弹窗要弹出什么组件
} else if (action.modalCfg.type === 'CustomModal') {
// 这里的actionid和组件是对应的,所以在按钮触发后,通过按钮携带的actionid能取到对应的组件。
componentKey.value = components[action.actionId];
custom.value.init(row);
}
经过以上的方式:在任何地方挂载都不会报错,完美解决。
注意:挂载与使用ref不能在同一个方法体内,如果可以的话,页面加载时,执行挂载,需要调用ref时就不会报错。
来源:https://blog.csdn.net/weixin_43865196/article/details/129286052


猜你喜欢
- 首先要明确,python里有json这个库,但并没有json这个类,所以所谓的json对象本质上就是一个dict;而json这个库,用于实现
- 首先画出流程图,流程图与现实代码有出入,因为刚开始画流程图的时候,有些东西没考虑进去,后来写着写着就慢慢能想起来并实现了。另有一点经验推荐给
- vue通信方式有很多,项目中用的比较多的的有 pros、vuex、$emit/$on 这3种,还有 provide/in
- 功能描述:1、右击节点可进行增删改2、可对节点数据进行模糊查询3、右击第一级节点可以进行同级节点增加4、双击节点或点击修改节点 都可以对节点
- 想大家都做过遮罩层这种常见的功能,css或jquery实现,实现方式多样化,这里http://我介绍我在项目中实现的方式,全屏遮罩,部分区域
- 五一在家写的,和大家分享,支持所有浏览器,添加了左侧菜单点击变色效果<!DOCTYPE html PUBLIC "-//W3
- Python程序可以处理多种方式的日期和时间。日期格式之间的转换是一种常见计算机的杂活。 Python的时间和日历模块,能帮助处
- 1、pyqtgraph库数据可视化效果还不错,特别是窗体程序中图像交互性较好;安装也很方便,用 pip 安装。2、在Python中新建一个
- 在 JavaScript 中,使用字符串连接有 几 种方式:连接符(+)、反引号(`)、join()、concat()。一、使用连接符 &a
- 今天来说下一台Linux主机如何启动4个MySQL数据库:1.要确定你的机器上面已经安装MySQL,我的MySQL在/usr/loacl/
- 本文实例讲述了mysql索引对排序的影响。分享给大家供大家参考,具体如下:索引不仅能提高查询速度,还可以添加排序速度,如果order by
- 1、作用删除指定长度的字符,并在指定的起点处插入另一组字符。2、语法STUFF ( character_expression , start
- 本文实例讲述了Python数据结构与算法之完全树与最小堆。分享给大家供大家参考,具体如下:# 完全树 最小堆class CompleteTr
- 这些帖子将分为三个部分。1.密码验证功能2.重构密码验证函数3.对密码验证功能进行单元测试这是Python系列中自定义密码验证的第三部分,也
- 使用Python方法比用各种命令方便,可以设置超时时间,到底通不通,端口是否开放一眼能看出来。命令和返回完整权限,可以ping通,端口开放,
- 如何更改 pandas dataframe 中两列的位置:把其中的某列移到第一列的位置。原来的 df 是:df = pd.read_csv(
- mysql-5.7.19-winx64 免安装版配置方法,供大家参考,具体内容如下1.官方网站下载mysql-5.7.19-winx64,注
- Djangos 内置的模板加载器(在先前的模板加载内幕章节有叙述)通常会满足你的所有的模板加载需求,但是如果你有特殊的加载需求的话,编写自己
- 最近在代码评审的过程,发现挺多错误使用eval导致代码注入的问题,比较典型的就是把eval当解析dict使用,有的就是简单的使用eval,有
- 0、项目介绍本篇将会以HandTrackingModule为模块,这里的模块与之前的有所不同,请按照本篇为准,前面的HandTracking