perf: optimize initial load by implementing lazy loading for modules and fine-tuning authentication/resource initialization logic.

This commit is contained in:
jxxghp
2026-05-14 13:19:48 +08:00
parent e2d36da299
commit 34124418f8
18 changed files with 345 additions and 178 deletions

View File

@@ -1,15 +1,12 @@
<script lang="ts" setup>
import draggable from 'vuedraggable'
import { useToast } from 'vue-toastification'
import api from '@/api'
import type { Plugin } from '@/api/types'
import NoDataFound from '@/components/NoDataFound.vue'
import PluginAppCard from '@/components/cards/PluginAppCard.vue'
import { getLogoUrl } from '@/utils/imageUtils'
import { useDisplay } from 'vuetify'
import { isNullOrEmptyObject } from '@/@core/utils'
import { getPluginTabs } from '@/router/i18n-menu'
import PluginMarketSettingDialog from '@/components/dialog/PluginMarketSettingDialog.vue'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
import PluginMixedSortCard from '@/components/cards/PluginMixedSortCard.vue'
@@ -22,6 +19,11 @@ const { t } = useI18n()
const route = useRoute()
// 市场卡片、拖拽排序和市场设置只在对应标签/操作中需要,延迟到真正使用时加载。
const Draggable = defineAsyncComponent(() => import('vuedraggable').then(module => module.default))
const PluginAppCard = defineAsyncComponent(() => import('@/components/cards/PluginAppCard.vue'))
const PluginMarketSettingDialog = defineAsyncComponent(() => import('@/components/dialog/PluginMarketSettingDialog.vue'))
// 显示器宽度
const display = useDisplay()
@@ -1536,7 +1538,7 @@ function onDragStartPlugin(evt: any) {
<!-- 混合排序列表文件夹和插件 -->
<template v-if="!currentFolder">
<!-- 主列表使用draggable进行混合排序 -->
<draggable
<Draggable
v-if="canDragSort"
v-model="mixedSortList"
@end="saveMixedSortOrder"
@@ -1565,7 +1567,7 @@ function onDragStartPlugin(evt: any) {
@drop-to-folder="(event, folderName) => handleDropToFolder(event, folderName)"
/>
</template>
</draggable>
</Draggable>
<ProgressiveCardGrid
v-else-if="shouldVirtualizeInstalledMainList"
:items="mixedSortList"
@@ -1598,7 +1600,7 @@ function onDragStartPlugin(evt: any) {
<template v-else>
<!-- 文件夹内使用draggable排序 + 移出按钮 -->
<draggable
<Draggable
v-if="canDragSort"
v-model="draggableFolderPlugins"
@end="saveFolderPluginOrder"
@@ -1624,7 +1626,7 @@ function onDragStartPlugin(evt: any) {
@remove-from-folder="removeFromFolder"
/>
</template>
</draggable>
</Draggable>
<ProgressiveCardGrid
v-else-if="shouldVirtualizeInstalledFolderList"
:items="draggableFolderPlugins"

View File

@@ -1,14 +1,10 @@
<!-- eslint-disable sonarjs/no-duplicate-string -->
<script lang="ts" setup>
import { useToast } from 'vue-toastification'
import draggable from 'vuedraggable'
import { VRow } from 'vuetify/lib/components/index.mjs'
import api from '@/api'
import { TransferDirectoryConf, StorageConf } from '@/api/types'
import DirectoryCard from '@/components/cards/DirectoryCard.vue'
import StorageCard from '@/components/cards/StorageCard.vue'
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
import CategoryEditDialog from '@/components/dialog/CategoryEditDialog.vue'
import { useI18n } from 'vue-i18n'
import { useTheme } from 'vuetify'
import { storageAttributes } from '@/api/constants'
@@ -16,6 +12,11 @@ import { storageAttributes } from '@/api/constants'
const { t } = useI18n()
const { global: globalTheme } = useTheme()
// 拖拽排序和分类编辑弹窗按需加载,避免设置框架预加载目录页时带上这些交互依赖。
const Draggable = defineAsyncComponent(() => import('vuedraggable').then(module => module.default))
const ProgressDialog = defineAsyncComponent(() => import('@/components/dialog/ProgressDialog.vue'))
const CategoryEditDialog = defineAsyncComponent(() => import('@/components/dialog/CategoryEditDialog.vue'))
// 所有下载目录
const directories = ref<TransferDirectoryConf[]>([])
@@ -264,7 +265,7 @@ onMounted(() => {
<VCardSubtitle>{{ t('setting.directory.storageDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
<Draggable
v-model="storages"
handle=".cursor-move"
item-key="name"
@@ -274,7 +275,7 @@ onMounted(() => {
<template #item="{ element }">
<StorageCard :storage="element" @close="removeStorage(element)" @done="loadStorages" />
</template>
</draggable>
</Draggable>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
@@ -309,7 +310,7 @@ onMounted(() => {
<VCardSubtitle>{{ t('setting.directory.directoryDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
<Draggable
v-model="directories"
handle=".cursor-move"
item-key="pri"
@@ -331,7 +332,7 @@ onMounted(() => {
@close="removeDirectory(element)"
/>
</template>
</draggable>
</Draggable>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">

View File

@@ -1,10 +1,8 @@
<script lang="ts" setup>
import { useToast } from 'vue-toastification'
import api from '@/api'
import draggable from 'vuedraggable'
import type { NotificationConf, NotificationSwitchConf } from '@/api/types'
import NotificationChannelCard from '@/components/cards/NotificationChannelCard.vue'
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
import { useI18n } from 'vue-i18n'
import { notificationSwitchDict } from '@/api/constants'
import { useTheme, useDisplay } from 'vuetify'
@@ -15,6 +13,10 @@ const display = useDisplay()
// 国际化
const { t } = useI18n()
// 通知渠道排序和进度弹窗按需加载,避免通知设置 chunk 直接包含拖拽库。
const Draggable = defineAsyncComponent(() => import('vuedraggable').then(module => module.default))
const ProgressDialog = defineAsyncComponent(() => import('@/components/dialog/ProgressDialog.vue'))
// 初始化模板配置字典
const templateConfigs = ref<Record<string, string>>({
organizeSuccess: '{}',
@@ -324,7 +326,7 @@ onMounted(() => {
<VCardSubtitle>{{ t('setting.notification.channelsDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
<Draggable
v-model="notifications"
handle=".cursor-move"
item-key="name"
@@ -339,7 +341,7 @@ onMounted(() => {
@close="removeNotification(element)"
/>
</template>
</draggable>
</Draggable>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">

View File

@@ -2,17 +2,19 @@
<script lang="ts" setup>
import { useToast } from 'vue-toastification'
import { copyToClipboard } from '@/@core/utils/navigator'
import draggable from 'vuedraggable'
import api from '@/api'
import { CustomRule, FilterRuleGroup } from '@/api/types'
import CustomerRuleCard from '@/components/cards/CustomRuleCard.vue'
import FilterRuleGroupCard from '@/components/cards/FilterRuleGroupCard.vue'
import ImportCodeDialog from '@/components/dialog/ImportCodeDialog.vue'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 拖拽库和导入弹窗只在规则编辑交互中需要,拆出设置页入口 chunk。
const Draggable = defineAsyncComponent(() => import('vuedraggable').then(module => module.default))
const ImportCodeDialog = defineAsyncComponent(() => import('@/components/dialog/ImportCodeDialog.vue'))
// 自定义规则列表
const customRules = ref<CustomRule[]>([])
@@ -381,7 +383,7 @@ onMounted(() => {
<VCardSubtitle>{{ t('setting.rule.customRulesDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
<Draggable
v-model="customRules"
handle=".cursor-move"
item-key="name"
@@ -396,7 +398,7 @@ onMounted(() => {
@change="onRuleChange"
/>
</template>
</draggable>
</Draggable>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
@@ -432,7 +434,7 @@ onMounted(() => {
<VCardSubtitle>{{ t('setting.rule.priorityRuleGroupsDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
<Draggable
v-model="filterRuleGroups"
handle=".cursor-move"
item-key="name"
@@ -449,7 +451,7 @@ onMounted(() => {
@change="changeRuleGroup"
/>
</template>
</draggable>
</Draggable>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">

View File

@@ -1,14 +1,11 @@
<!-- eslint-disable sonarjs/no-duplicate-string -->
<script lang="ts" setup>
import { useToast } from 'vue-toastification'
import { VRow } from 'vuetify/lib/components/index.mjs'
import draggable from 'vuedraggable'
import api from '@/api'
import { DownloaderConf, MediaServerConf } from '@/api/types'
import DownloaderCard from '@/components/cards/DownloaderCard.vue'
import MediaServerCard from '@/components/cards/MediaServerCard.vue'
import { copyToClipboard } from '@/@core/utils/navigator'
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
import { useI18n } from 'vue-i18n'
import { downloaderOptions, mediaServerOptions } from '@/api/constants'
import { useDisplay, useTheme } from 'vuetify'
@@ -22,6 +19,10 @@ const isTransparentTheme = computed(() => theme.name.value === 'transparent')
// 国际化
const { t } = useI18n()
// 下载器/媒体服务器排序和进度弹窗按需加载,降低系统设置页入口解析量。
const Draggable = defineAsyncComponent(() => import('vuedraggable').then(module => module.default))
const ProgressDialog = defineAsyncComponent(() => import('@/components/dialog/ProgressDialog.vue'))
// 系统设置项
const SystemSettings = ref<any>({
// 基础设置
@@ -1405,7 +1406,7 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
<VCardSubtitle>{{ t('setting.system.downloadersDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
<Draggable
v-model="downloaders"
handle=".cursor-move"
item-key="name"
@@ -1421,7 +1422,7 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
:allow-refresh="isRequest"
/>
</template>
</draggable>
</Draggable>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
@@ -1456,7 +1457,7 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
<VCardSubtitle>{{ t('setting.system.mediaServersDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
<Draggable
v-model="mediaServers"
handle=".cursor-move"
item-key="name"
@@ -1471,7 +1472,7 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
@change="onMediaServerChange"
/>
</template>
</draggable>
</Draggable>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">

View File

@@ -1,12 +1,8 @@
<script lang="ts" setup>
import draggable from 'vuedraggable'
import api from '@/api'
import type { Site, SiteUserData } from '@/api/types'
import SiteCard from '@/components/cards/SiteCard.vue'
import NoDataFound from '@/components/NoDataFound.vue'
import SiteAddEditDialog from '@/components/dialog/SiteAddEditDialog.vue'
import SiteStatisticsDialog from '@/components/dialog/SiteStatisticsDialog.vue'
import SiteImportDialog from '@/components/dialog/SiteImportDialog.vue'
import ProgressiveCardGrid from '@/components/misc/ProgressiveCardGrid.vue'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
@@ -25,6 +21,12 @@ const route = useRoute()
// APP 模式检测
const { appMode } = usePWA()
// 拖拽排序和站点弹窗都不是站点列表首屏必需,打开对应功能时再加载。
const Draggable = defineAsyncComponent(() => import('vuedraggable').then(module => module.default))
const SiteAddEditDialog = defineAsyncComponent(() => import('@/components/dialog/SiteAddEditDialog.vue'))
const SiteStatisticsDialog = defineAsyncComponent(() => import('@/components/dialog/SiteStatisticsDialog.vue'))
const SiteImportDialog = defineAsyncComponent(() => import('@/components/dialog/SiteImportDialog.vue'))
// 站点列表
const siteList = ref<Site[]>([])
@@ -402,7 +404,7 @@ useDynamicButton({
</VAlert>
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
<draggable
<Draggable
v-if="draggableSiteList.length > 0 && canDragSort"
v-model="draggableSiteList"
@end="savaSitesPriority"
@@ -421,7 +423,7 @@ useDynamicButton({
@refresh-stats="handleRefreshStats"
/>
</template>
</draggable>
</Draggable>
<ProgressiveCardGrid
v-else-if="draggableSiteList.length > 0 && shouldVirtualizeList"
:items="draggableSiteList"