Nuxt3组件首次渲染加载器设置及Element Plus标签切换卡顿解决
解决方案:Nuxt3 + Element Plus 标签页组件首次切换延迟与加载器实现
问题原因
你遇到的首次切换延迟,本质是Lazy组件的按需加载机制导致的:Nuxt的LazyComponent语法糖会把组件打包成独立的chunk文件,只有当组件被首次渲染(v-if条件满足)时,才会发起网络请求加载对应的chunk资源,这中间的网络请求和解析时间就是你感知到的延迟。
解决延迟的两种核心方案
1. 提前预加载组件
通过主动触发组件chunk的加载,避免用户点击时的等待。
方案A:页面挂载后后台预加载
在页面挂载完成后,异步加载另外两个组件的chunk,这样用户切换时资源已经加载完毕:
<script setup> const activeTabName = ref("tab1"); // 标记组件是否已加载 const loadedTabs = reactive({ tab1: true, tab2: false, tab3: false }); // 预加载组件函数 const preloadTabComponent = async (tabName) => { if (!loadedTabs[tabName]) { // 路径根据你的组件实际位置调整 switch(tabName) { case 'tab2': await import('~/components/Tab2.vue'); break; case 'tab3': await import('~/components/Tab3.vue'); break; } loadedTabs[tabName] = true; } }; onMounted(async () => { await getDetails(); // 后台预加载tab2和tab3的组件,不阻塞页面渲染 Promise.all([preloadTabComponent('tab2'), preloadTabComponent('tab3')]); }); </script>
方案B:标签hover时预加载
在用户鼠标悬停到非激活标签时触发预加载,更贴近用户操作场景:
<template> <el-tabs v-model="activeTabName"> <el-tab-pane label="Tab 1" name="tab1"> <!-- ... --> </el-tab-pane> <el-tab-pane label="Tab 2" name="tab2" @mouseenter="preloadTabComponent('tab2')"> <!-- ... --> </el-tab-pane> <el-tab-pane label="Tab 3" name="tab3" @mouseenter="preloadTabComponent('tab3')"> <!-- ... --> </el-tab-pane> </el-tabs> </template>
2. 优化组件体积
如果组件本身体积过大,也会导致加载延迟,可通过以下方式优化:
- 拆分组件内的大模块(比如复杂的表单校验逻辑、大型图表)为更小的子组件
- 移除组件内未使用的Element Plus组件(Nuxt3会自动树摇,但手动清理更彻底)
- 对组件内的静态资源(如图片)进行压缩
实现组件首次渲染加载器
通过跟踪组件的加载状态,在组件未加载完成时显示加载动画,提升用户体验:
修改后的完整代码
<template> <el-tabs v-model="activeTabName"> <el-tab-pane label="Tab 1" name="tab1"> <LazyTab1 v-if="activeTabName == 'tab1'" :form-data="formData" :loader="loader" @on-submit="submitForm" /> </el-tab-pane> <el-tab-pane label="Tab 2" name="tab2" @mouseenter="preloadTabComponent('tab2')"> <!-- 加载状态显示 --> <div v-if="activeTabName == 'tab2' && !loadedTabs.tab2" class="tab-loader"> <el-spinner size="40" /> <p>加载中...</p> </div> <LazyTab2 v-else-if="activeTabName == 'tab2'" :form-data="formData" :loader="loader" @submit-form="submitForm" @loaded="loadedTabs.tab2 = true" /> </el-tab-pane> <el-tab-pane label="Tab 3" name="tab3" @mouseenter="preloadTabComponent('tab3')"> <div v-if="activeTabName == 'tab3' && !loadedTabs.tab3" class="tab-loader"> <el-spinner size="40" /> <p>加载中...</p> </div> <LazyTab3 v-else-if="activeTabName == 'tab3'" :form-data="formData" :loader="loader" @on-submit="submitForm" @loaded="loadedTabs.tab3 = true" /> </el-tab-pane> </el-tabs> </template> <script setup> import { ref, reactive, onMounted } from 'vue'; const activeTabName = ref("tab1"); const loadedTabs = reactive({ tab1: true, tab2: false, tab3: false }); // 修正原代码笔误:重复的tab2改为tab3 const formData = reactive({ tab1: { // ... }, tab2: { // ... }, tab3: { // ... } }); const preloadTabComponent = async (tabName) => { if (!loadedTabs[tabName]) { try { // 路径根据你的组件实际位置调整 switch(tabName) { case 'tab2': await import('~/components/Tab2.vue'); break; case 'tab3': await import('~/components/Tab3.vue'); break; } loadedTabs[tabName] = true; } catch (e) { console.error(`预加载${tabName}组件失败`, e); } } }; const submitForm = async (formRef) => { // ...提交逻辑 }; const getDetails = async () => { // ...API请求逻辑 }; onMounted(async () => { await getDetails(); // 可选:后台预加载,可根据业务需求决定是否开启 // Promise.all([preloadTabComponent('tab2'), preloadTabComponent('tab3')]); }); </script> <style scoped> .tab-loader { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 300px; gap: 12px; } </style>
补充说明
- 子组件需添加
loaded事件:在Tab2、Tab3组件的onMounted钩子中执行$emit('loaded'),告知父组件已加载完成。 - 也可以用Element Plus的
v-loading指令替代自定义加载器,直接给tab-pane的容器添加v-loading="!loadedTabs.tab2"。
内容的提问来源于stack exchange,提问作者Kishan Bhensadadiya
相关产品推荐
相关产品推荐

