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、不能在组件的事件函数内挂载,

vue3 使用defineAsyncComponent与component标签实现动态渲染组件思路详解

2、不能在axios的then函数体内挂载

vue3 使用defineAsyncComponent与component标签实现动态渲染组件思路详解

3、不能在带有async声明的函数体内挂载

vue3 使用defineAsyncComponent与component标签实现动态渲染组件思路详解

4、不能在vue的生命周期内挂载

vue3 使用defineAsyncComponent与component标签实现动态渲染组件思路详解

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

标签:vue3,动态渲染组件,defineAsyncComponent,component
0
投稿

猜你喜欢

  • SQL学习笔记三 select语句的各种形式小结

    2011-09-30 11:09:31
  • JavaScript中遍历对象的property的3种方法介绍

    2024-06-05 09:31:19
  • JavaScript阻止浏览器返回按钮的方法

    2024-02-25 16:15:44
  • python 递归调用返回None的问题及解决方法

    2022-12-21 05:52:56
  • Python2和Python3中print的用法示例总结

    2022-07-19 02:27:58
  • Go语言基础go install命令使用示例详解

    2024-04-26 17:25:48
  • matplotlib绘制多个子图(subplot)的方法

    2023-01-17 08:27:45
  • python操作xlsx格式文件并读取

    2021-07-01 20:03:26
  • 深入了解Python中yield from语法的使用

    2022-05-18 08:24:17
  • SQLServer 跨库查询实现方法

    2012-04-13 12:07:09
  • python模拟点击在ios中实现的实例讲解

    2021-11-28 13:03:34
  • Python实现简单的文件操作合集

    2022-03-25 05:00:35
  • 教你为MySQL数据库换挡加速

    2010-03-03 16:58:00
  • 初步讲解Python中的元组概念

    2022-06-03 23:05:55
  • SQL语句练习实例之三——平均销售等待时间

    2011-10-24 20:11:47
  • linux 部署apache服务的步骤

    2022-11-28 02:59:16
  • GOLANG版的冒泡排序和快速排序分享

    2023-07-05 05:31:09
  • 使用Python获取网段IP个数以及地址清单的方法

    2021-02-25 03:28:21
  • Python 变量的创建过程详解

    2022-04-20 03:01:17
  • 在阿里云服务器上配置CentOS+Nginx+Python+Flask环境

    2023-07-26 09:47:46
  • asp之家 网络编程 m.aspxhome.com