Vue3项目,子组件默认加载了两次,使用 defineAsyncComponent 引入组件后只加载一次
文章目录
- 一、场景描述
- 二、组件为什么会加载两次?
- 三、使用 defineAsyncComponent 后为何只加载一次?
- 四、总结对比
在 Vue 3 开发中,可能会遇到这样的问题:某个子组件在页面加载时“看起来”被创建了两次。多少有点让人疑惑,也可能影响性能和逻辑的正确性。
一、场景描述
子组件使用方式,示例代码:
// template
<Childv-if="!detailLoading"ref="childRef"v-model:info="info"
/>// script
import Child from './components/Child.vue';// 在请求详情接口前设置 detailLoading.value = true;接口完成后,在 finally 中设置 detailLoading.value = false
const detailLoading = ref(true);
const getModelDetail = async () => {detailLoading.value = true;try {const res = await xxx;info.value = res;} finally {detailLoading.value = false;}
};
结果:Child 组件被加载了 两次
二、组件为什么会加载两次?
这个问题的核心在于 Vue 的响应式系统和组件生命周期行为,尤其是与 v-if、ref 和组件引入方式有关。
1. v-if 控制组件渲染时机
因为我们使用了 v-if=“!detailLoading” 来控制 < Child > 是否渲染:
<Child v-if="!detailLoading" />
也就是说:
- 初始时 detailLoading 是 true,所以组件不会渲染。
- 请求完成后设置 detailLoading = false,触发组件首次渲染。
- 如果中间状态变化导致 detailLoading 再次变为 true,再变回 false,组件就会再次重新创建。
- 所以如果我们在代码中不小心多次修改了 detailLoading 的值,或者初始值处理不当,组件就会被创建多次。
2. import 引入组件是同步的
import Child from './components/Child.vue';
这是标准的 ES Module 导入方式,模块在应用启动时就会被加载并执行一次。但注意:导入本身只执行一次,不代表组件实例只创建一次。
每次 v-if 变为 true,Vue 都会重新创建一个新的组件实例。因此:
组件加载次数 = v-if 从 false 变为 true 的次数
三、使用 defineAsyncComponent 后为何只加载一次?
更改引入组件的方式:
const Child = defineAsyncComponent(() =>import('./components/Child.vue')
);
此时发现组件只加载了一次。
原因分析:
- defineAsyncComponent是懒加载机制
- 它会在组件第一次需要渲染时才去异步加载组件文件。
- 加载完成后,组件会被缓存,后续再次渲染时不会再发起新的网络请求。
- Vue 内部做了缓存优化
- 当使用 defineAsyncComponent 包裹动态导入时,Vue 会自动缓存这个异步组件的结果。即使你多次切换 v-if 的值,它也不会重复加载组件文件。
也就是说:
虽然组件实例可能会被销毁重建(由 v-if 控制),但组件文件只加载一次。
四、总结对比
特性 | import 直接引入 | defineAsyncComponent + import() |
---|---|---|
组件文件加载方式 | 同步加载,初始化时即加载 | 异步加载,按需加载 |
文件是否重复加载 | 不会(模块单例) | 不会(内部缓存) |
组件实例是否重复创建 | 是(由 v-if 控制) | 是(由 v-if 控制) |
适合场景 | 小型组件或首屏必须使用的组件 | 大型组件、非首屏组件、懒加载优化 |
总结:
- 使用 import 引入的组件只会加载一次模块,但实例可能被多次创建。
- 使用 defineAsyncComponent 可以延迟加载组件,并利用 Vue 的缓存机制避免重复请求。
- 实际开发中应根据组件大小、使用频率选择合适的加载方式。