异步加载的组件
代码
typescript
import { ref, defineAsyncComponent, watch, type Component } from 'vue'
interface TabsConfigItem {
key: ENUM_DETAIL_TAB
tab: string
component: Component
}
// Tab 栏
export const enum ENUM_DETAIL_TAB {
accountDetail = 'accountDetail',
relatedTransaction = 'relatedTransaction',
historicalCase = 'historicalCase',
processLog = 'processLog'
}
/**
* @constant tabsConfig
* @description 动态组件的核心配置
* 定义了每个 Tab 的唯一 key(引用枚举值)、显示的标题以及对应的异步加载组件。
*/
const tabsConfig: TabsConfigItem[] = [
// 账户详情
{
key: ENUM_DETAIL_TAB.accountDetail,
tab: riskManagementT('accountDetail'),
component: defineAsyncComponent(() => import('./accountDetail.vue'))
},
// 关联交易
{
key: ENUM_DETAIL_TAB.relatedTransaction,
tab: riskManagementT('relatedTransaction'),
component: defineAsyncComponent(() => import('./relatedTransaction.vue'))
},
// 历史案件
{
key: ENUM_DETAIL_TAB.historicalCase,
tab: riskManagementT('historicalCase'),
component: defineAsyncComponent(() => import('./historicalCase.vue'))
},
// 处理日志
{
key: ENUM_DETAIL_TAB.processLog,
tab: riskManagementT('processLog'),
component: defineAsyncComponent(() => import('./processLog.vue'))
}
]
// 从核心配置中派生出 Tab 列表和组件映射
const { tabList, tabComponentsMap } = tabsConfig.reduce(
(acc, tab) => {
const { key, tab: tabTitle, component } = tab
acc.tabList.push({ key, tab: tabTitle })
acc.tabComponentsMap[key] = component
return acc
},
{
tabList: [] as { key: ENUM_DETAIL_TAB; tab: string }[],
tabComponentsMap: {} as Record<ENUM_DETAIL_TAB, Component>
}
)
// 当前激活的 Tab,默认为账户详情
const activeKey = ref<ENUM_DETAIL_TAB>(ENUM_DETAIL_TAB.accountDetail)
defineAsyncComponent是 Vue 3 的 Composition API 中,用来定义异步组件的函数。
它的作用可以概括为:让组件在需要时才加载,而不是一开始就打包进主包,从而减小首屏体积、提升加载速度。
1. 基本作用
-
把组件变成一个异步加载的组件:
在路由或父组件渲染时,才会去请求并加载这个组件的代码。
-
内部基于 动态
import() 实现,配合构建工具(Vite/Webpack)自动做代码分割(code splitting) 。
2. 基本用法
javascript
import { defineAsyncComponent } from 'vue'
// 方式1:简单动态 import
const AsyncComp = defineAsyncComponent(() => import('./MyComponent.vue'))
// 方式2:带加载/错误状态的配置
const AsyncComp = defineAsyncComponent({
loader: () => import('./MyComponent.vue'),
loadingComponent: LoadingSpinner, // 加载中显示的组件
errorComponent: ErrorDisplay, // 加载失败时显示的组件
delay: 200, // 延迟多少 ms 才显示 loading
timeout: 3000 // 超时时间
})
3. 使用场景
-
路由懒加载:
javascriptconst routes = [ { path: '/dashboard', component: () => import('./views/Dashboard.vue') } ]在路由配置里,其实也是用了类似的异步组件思想。
-
大型页面/低频功能:
比如"报表弹窗""富文本编辑器"等,只有用户点开时才加载,减少首屏 JS 体积。
-
需要加载状态/错误处理时:
用
loadingComponent和errorComponent给用户更好的体验。
4. 和 import()的区别
import()返回的是Promise<Component>,需要你自己处理加载/错误状态。defineAsyncComponent是对import()的封装,帮你更方便地配置加载中、错误、超时等行为,并且在 Vue 组件树里使用时更符合 Vue 的生命周期管理。
✅ 一句话总结:
defineAsyncComponent的作用是 把组件变成按需异步加载的组件,既能减小首屏体积,又能优雅处理加载中和加载失败的情况,是 Vue 3 性能优化的重要工具。
动态插槽
通过 v-for 循环动态生成所有需要批量输入的具名插槽。#[item.field] 是 Vue 的动态插槽名语法,它会根据 item.field 的值(如 'icAccountList')来决定要渲染哪个插槽,从而避免了在模板中重复编写每个插槽的 template。
原代码
xml
<!-- 发卡账户 -->
<template #icAccount="{ model, field }">
<a-input-group compact>
<a-input
v-model:value.trim="model[field]"
:placeholder="dataT('icAccount')"
allow-clear
/>
<a-button type="primary" class="border-5" @click="openBatchInputModal(field)">
<template #icon>
<PlusCircleOutlined />
</template>
</a-button>
</a-input-group>
</template>
<!-- 企业账号 -->
<template #opIdList="{ model, field }">
<a-input-group compact>
<a-input
v-model:value.trim="model[field]"
:placeholder="dataT('opIds')"
allow-clear
/>
<a-button type="primary" class="border-5" @click="openBatchInputModal(field)">
<template #icon>
<PlusCircleOutlined />
</template>
</a-button>
</a-input-group>
</template>
优化后
ini
<template
v-for="item in editingList"
:key="item.field"
#[item.field]="{ model, field }"
>
<a-input-group compact>
<a-input
v-model:value.trim="model[field]"
:placeholder="item?.placeholder"
allow-clear
@input="handleInput($event, field)"
/>
<a-button type="primary" class="border-5" @click="openBatchInputModal(field)">
<template #icon>
<PlusCircleOutlined />
</template>
</a-button>
</a-input-group>
</template>