<template v-for="(item, index) in checkList" :key="index">
<a-tab-pane :tab="item.tabName">
<Suspense>
<component
:is="
DefineAsyncComponent(
() =>
import(
systemList.indexOf(item.componentName) !== -1
? `./detail/${item.componentName}.vue`
: `../../developComponents/${item.componentName}/index.vue`
),
{
loading: true,
},
)
"
:rev-data="show"
:key="item.id"
v-if="show && dataId && objectType"
:data="item"
@success="success"
@refresh="refreshData"
@changeShowFilter="changeShowFilter"
@close="close"
:isFilterEmpty="isFilterEmpty"
:isExportPdf="isExportPdf"
class="check-component"
:class="[
`${checkList[activeKey]['componentName'] === 'BaseInfo' ? 'pdfDom' : ''}`,
`${isExportPdf ? 'pdf-Class' : ''}`,
]"
/>
<template #fallback>
<a-skeleton active />
</template>
</Suspense>
</a-tab-pane>
</template>
在 Vue 中,如果你直接在 :is
中调用 defineAsyncComponent
,例如:
vue
复制
<component :is="defineAsyncComponent(() => import('./MyComponent.vue'))" />
这种情况下,组件不会被缓存,每次渲染时都会重新加载。这是因为每次渲染时,defineAsyncComponent
都会创建一个新的异步组件实例,而不是复用之前的实例。
1. 为什么不会缓存?
原因 1:每次渲染都会重新调用 defineAsyncComponent
-
在 Vue 的模板中,
:is
绑定的表达式会在每次渲染时重新计算。 -
如果你直接调用
defineAsyncComponent
,每次渲染时都会创建一个新的异步组件实例,而不是复用之前的实例。
原因 2:defineAsyncComponent
返回一个新的组件
-
defineAsyncComponent
是一个工厂函数,每次调用都会返回一个新的异步组件实例。 -
即使加载的是同一个组件文件,Vue 也会将其视为不同的组件实例。
原因 3:没有显式的缓存机制
-
直接调用
defineAsyncComponent
时,没有显式的缓存逻辑来存储已经加载的组件实例。 -
因此,每次渲染时都会重新加载组件。
2. 示例分析
以下代码会导致每次渲染时重新加载组件:
vue
复制
<template> <component :is="getAsyncComponent('MyComponent')" /> </template> <script> import { defineAsyncComponent } from 'vue'; export default { methods: { getAsyncComponent(componentName) { return defineAsyncComponent(() => import(`./components/${componentName}.vue`) ); }, }, }; </script>
问题:
-
每次渲染时,
getAsyncComponent
方法都会被调用。 -
每次调用
getAsyncComponent
时,都会创建一个新的异步组件实例。 -
即使
componentName
相同,Vue 也会重新加载组件。
3. 如何实现缓存?
为了实现缓存,需要显式地存储已经加载的组件实例,并在后续渲染时复用这些实例。可以通过以下方式实现:
方法 1:使用全局缓存对象
在组件外部定义一个缓存对象,存储已经加载的组件实例:
javascript
复制
const componentCache = {}; const getAsyncComponent = (componentName) => { if (componentCache[componentName]) { return componentCache[componentName]; } return componentCache[componentName] = defineAsyncComponent(() => import(`./components/${componentName}.vue`) ); };
在模板中使用缓存后的组件:
vue
复制
<template> <component :is="getAsyncComponent('MyComponent')" /> </template> <script> import { defineAsyncComponent } from 'vue'; const componentCache = {}; const getAsyncComponent = (componentName) => { if (componentCache[componentName]) { return componentCache[componentName]; } return componentCache[componentName] = defineAsyncComponent(() => import(`./components/${componentName}.vue`) ); }; export default { methods: { getAsyncComponent, }, }; </script>
方法 2:使用 keep-alive
<keep-alive>
是 Vue 提供的内置组件,可以缓存动态组件的实例,避免组件被销毁和重新创建:
vue
复制
<keep-alive> <component :is="getAsyncComponent('MyComponent')" /> </keep-alive>
方法 3:将异步组件提升到全局
将异步组件定义在组件外部,确保只加载一次:
javascript
复制
const MyComponent = defineAsyncComponent(() => import('./components/MyComponent.vue') ); export default { components: { MyComponent, }, };
在模板中直接使用组件名:
vue
复制
<component :is="MyComponent" />
4. 直接调用 defineAsyncComponent
的问题
如果你直接调用 defineAsyncComponent
,例如:
vue
复制
<component :is="defineAsyncComponent(() => import('./MyComponent.vue'))" />
每次渲染时都会发生以下过程:
-
调用
defineAsyncComponent
,创建一个新的异步组件实例。 -
触发动态导入 (
import()
),加载组件文件。 -
组件加载完成后,渲染到页面上。
由于每次渲染都会创建一个新的异步组件实例,因此无法复用之前的实例,导致组件重新加载。
5. 总结
-
直接在
:is
中调用defineAsyncComponent
不会缓存组件,因为每次渲染都会创建一个新的异步组件实例。 -
要实现缓存,需要显式地存储已经加载的组件实例(例如使用全局缓存对象),或者使用
<keep-alive>
来缓存组件实例。 -
通过缓存机制,可以避免重复加载组件,提升性能。