mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-10 15:40:24 +00:00
feat: code splitting optimization - reduce initial bundle by 36% (#8542)
## Summary Reduces initial module preload from 12.94 MB to 8.24 MB (-4.7 MB, -36%). ## Changes - Split vendor chunks for better cache isolation (firebase, sentry, i18n, zod, etc.) - Exclude heavy optional chunks from initial preload: THREE.js, xterm, tiptap, chart.js, yjs - Lazy load 16 dialog components in dialogService - Add \endor-yjs\ chunk for CRDT library ## Metrics | Metric | Before | After | |--------|--------|-------| | Initial preload | 12.94 MB | 8.24 MB | | vendor-other | 4,006 KB | 2,156 KB | | dialogService | 1,860 KB | 1,304 KB | All excluded chunks still load on-demand when their features are used. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8542-feat-code-splitting-optimization-reduce-initial-bundle-by-36-2fb6d73d36508146aaf7fdaed3274033) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -1,39 +1,62 @@
|
||||
import { merge } from 'es-toolkit/compat'
|
||||
import type { Component } from 'vue'
|
||||
|
||||
import ApiNodesSignInContent from '@/components/dialog/content/ApiNodesSignInContent.vue'
|
||||
import MissingNodesContent from '@/components/dialog/content/MissingNodesContent.vue'
|
||||
import MissingNodesFooter from '@/components/dialog/content/MissingNodesFooter.vue'
|
||||
import MissingNodesHeader from '@/components/dialog/content/MissingNodesHeader.vue'
|
||||
import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.vue'
|
||||
import ErrorDialogContent from '@/components/dialog/content/ErrorDialogContent.vue'
|
||||
import MissingModelsWarning from '@/components/dialog/content/MissingModelsWarning.vue'
|
||||
import PromptDialogContent from '@/components/dialog/content/PromptDialogContent.vue'
|
||||
import SignInContent from '@/components/dialog/content/SignInContent.vue'
|
||||
import TopUpCreditsDialogContent from '@/components/dialog/content/TopUpCreditsDialogContent.vue'
|
||||
import UpdatePasswordContent from '@/components/dialog/content/UpdatePasswordContent.vue'
|
||||
import ComfyOrgHeader from '@/components/dialog/header/ComfyOrgHeader.vue'
|
||||
import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue'
|
||||
import { t } from '@/i18n'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import type {
|
||||
DialogComponentProps,
|
||||
ShowDialogOptions
|
||||
} from '@/stores/dialogStore'
|
||||
|
||||
import ImportFailedNodeContent from '@/workbench/extensions/manager/components/manager/ImportFailedNodeContent.vue'
|
||||
import ImportFailedNodeFooter from '@/workbench/extensions/manager/components/manager/ImportFailedNodeFooter.vue'
|
||||
import ImportFailedNodeHeader from '@/workbench/extensions/manager/components/manager/ImportFailedNodeHeader.vue'
|
||||
import NodeConflictDialogContent from '@/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue'
|
||||
import NodeConflictFooter from '@/workbench/extensions/manager/components/manager/NodeConflictFooter.vue'
|
||||
import NodeConflictHeader from '@/workbench/extensions/manager/components/manager/NodeConflictHeader.vue'
|
||||
import type { ConflictDetectionResult } from '@/workbench/extensions/manager/types/conflictDetectionTypes'
|
||||
import type { ComponentAttrs } from 'vue-component-type-helpers'
|
||||
|
||||
// Type-only imports for ComponentAttrs inference (no runtime cost)
|
||||
import type MissingNodesContent from '@/components/dialog/content/MissingNodesContent.vue'
|
||||
import type MissingModelsWarning from '@/components/dialog/content/MissingModelsWarning.vue'
|
||||
|
||||
// Lazy loaders for dialogs - components are loaded on first use
|
||||
const lazyMissingNodesContent = () =>
|
||||
import('@/components/dialog/content/MissingNodesContent.vue')
|
||||
const lazyMissingNodesHeader = () =>
|
||||
import('@/components/dialog/content/MissingNodesHeader.vue')
|
||||
const lazyMissingNodesFooter = () =>
|
||||
import('@/components/dialog/content/MissingNodesFooter.vue')
|
||||
const lazyMissingModelsWarning = () =>
|
||||
import('@/components/dialog/content/MissingModelsWarning.vue')
|
||||
const lazyApiNodesSignInContent = () =>
|
||||
import('@/components/dialog/content/ApiNodesSignInContent.vue')
|
||||
const lazySignInContent = () =>
|
||||
import('@/components/dialog/content/SignInContent.vue')
|
||||
const lazyTopUpCreditsDialogContent = () =>
|
||||
import('@/components/dialog/content/TopUpCreditsDialogContent.vue')
|
||||
const lazyUpdatePasswordContent = () =>
|
||||
import('@/components/dialog/content/UpdatePasswordContent.vue')
|
||||
const lazyComfyOrgHeader = () =>
|
||||
import('@/components/dialog/header/ComfyOrgHeader.vue')
|
||||
const lazySettingDialogHeader = () =>
|
||||
import('@/components/dialog/header/SettingDialogHeader.vue')
|
||||
const lazySettingDialogContent = () =>
|
||||
import('@/platform/settings/components/SettingDialogContent.vue')
|
||||
const lazyImportFailedNodeContent = () =>
|
||||
import('@/workbench/extensions/manager/components/manager/ImportFailedNodeContent.vue')
|
||||
const lazyImportFailedNodeHeader = () =>
|
||||
import('@/workbench/extensions/manager/components/manager/ImportFailedNodeHeader.vue')
|
||||
const lazyImportFailedNodeFooter = () =>
|
||||
import('@/workbench/extensions/manager/components/manager/ImportFailedNodeFooter.vue')
|
||||
const lazyNodeConflictDialogContent = () =>
|
||||
import('@/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue')
|
||||
const lazyNodeConflictHeader = () =>
|
||||
import('@/workbench/extensions/manager/components/manager/NodeConflictHeader.vue')
|
||||
const lazyNodeConflictFooter = () =>
|
||||
import('@/workbench/extensions/manager/components/manager/NodeConflictFooter.vue')
|
||||
|
||||
export type ConfirmationDialogType =
|
||||
| 'default'
|
||||
| 'overwrite'
|
||||
@@ -58,9 +81,19 @@ export interface ExecutionErrorDialogInput {
|
||||
export const useDialogService = () => {
|
||||
const dialogStore = useDialogStore()
|
||||
|
||||
function showLoadWorkflowWarning(
|
||||
async function showLoadWorkflowWarning(
|
||||
props: ComponentAttrs<typeof MissingNodesContent>
|
||||
) {
|
||||
const [
|
||||
{ default: MissingNodesContent },
|
||||
{ default: MissingNodesHeader },
|
||||
{ default: MissingNodesFooter }
|
||||
] = await Promise.all([
|
||||
lazyMissingNodesContent(),
|
||||
lazyMissingNodesHeader(),
|
||||
lazyMissingNodesFooter()
|
||||
])
|
||||
|
||||
dialogStore.showDialog({
|
||||
key: 'global-missing-nodes',
|
||||
headerComponent: MissingNodesHeader,
|
||||
@@ -84,9 +117,10 @@ export const useDialogService = () => {
|
||||
})
|
||||
}
|
||||
|
||||
function showMissingModelsWarning(
|
||||
async function showMissingModelsWarning(
|
||||
props: ComponentAttrs<typeof MissingModelsWarning>
|
||||
) {
|
||||
const { default: MissingModelsWarning } = await lazyMissingModelsWarning()
|
||||
dialogStore.showDialog({
|
||||
key: 'global-missing-models-warning',
|
||||
component: MissingModelsWarning,
|
||||
@@ -94,7 +128,7 @@ export const useDialogService = () => {
|
||||
})
|
||||
}
|
||||
|
||||
function showSettingsDialog(
|
||||
async function showSettingsDialog(
|
||||
panel?:
|
||||
| 'about'
|
||||
| 'keybinding'
|
||||
@@ -106,6 +140,14 @@ export const useDialogService = () => {
|
||||
| 'workspace'
|
||||
| 'secrets'
|
||||
) {
|
||||
const [
|
||||
{ default: SettingDialogHeader },
|
||||
{ default: SettingDialogContent }
|
||||
] = await Promise.all([
|
||||
lazySettingDialogHeader(),
|
||||
lazySettingDialogContent()
|
||||
])
|
||||
|
||||
const props = panel ? { props: { defaultPanel: panel } } : undefined
|
||||
|
||||
dialogStore.showDialog({
|
||||
@@ -116,7 +158,15 @@ export const useDialogService = () => {
|
||||
})
|
||||
}
|
||||
|
||||
function showAboutDialog() {
|
||||
async function showAboutDialog() {
|
||||
const [
|
||||
{ default: SettingDialogHeader },
|
||||
{ default: SettingDialogContent }
|
||||
] = await Promise.all([
|
||||
lazySettingDialogHeader(),
|
||||
lazySettingDialogContent()
|
||||
])
|
||||
|
||||
dialogStore.showDialog({
|
||||
key: 'global-settings',
|
||||
headerComponent: SettingDialogHeader,
|
||||
@@ -223,6 +273,9 @@ export const useDialogService = () => {
|
||||
async function showApiNodesSignInDialog(
|
||||
apiNodeNames: string[]
|
||||
): Promise<boolean> {
|
||||
const [{ default: ApiNodesSignInContent }, { default: ComfyOrgHeader }] =
|
||||
await Promise.all([lazyApiNodesSignInContent(), lazyComfyOrgHeader()])
|
||||
|
||||
return new Promise<boolean>((resolve) => {
|
||||
dialogStore.showDialog({
|
||||
key: 'api-nodes-signin',
|
||||
@@ -245,6 +298,9 @@ export const useDialogService = () => {
|
||||
}
|
||||
|
||||
async function showSignInDialog(): Promise<boolean> {
|
||||
const [{ default: SignInContent }, { default: ComfyOrgHeader }] =
|
||||
await Promise.all([lazySignInContent(), lazyComfyOrgHeader()])
|
||||
|
||||
return new Promise<boolean>((resolve) => {
|
||||
dialogStore.showDialog({
|
||||
key: 'global-signin',
|
||||
@@ -340,12 +396,15 @@ export const useDialogService = () => {
|
||||
})
|
||||
}
|
||||
|
||||
function showTopUpCreditsDialog(options?: {
|
||||
async function showTopUpCreditsDialog(options?: {
|
||||
isInsufficientCredits?: boolean
|
||||
}) {
|
||||
const { isActiveSubscription } = useSubscription()
|
||||
if (!isActiveSubscription.value) return
|
||||
|
||||
const { default: TopUpCreditsDialogContent } =
|
||||
await lazyTopUpCreditsDialogContent()
|
||||
|
||||
return dialogStore.showDialog({
|
||||
key: 'top-up-credits',
|
||||
component: TopUpCreditsDialogContent,
|
||||
@@ -364,7 +423,10 @@ export const useDialogService = () => {
|
||||
/**
|
||||
* Shows a dialog for updating the current user's password.
|
||||
*/
|
||||
function showUpdatePasswordDialog() {
|
||||
async function showUpdatePasswordDialog() {
|
||||
const [{ default: UpdatePasswordContent }, { default: ComfyOrgHeader }] =
|
||||
await Promise.all([lazyUpdatePasswordContent(), lazyComfyOrgHeader()])
|
||||
|
||||
return dialogStore.showDialog({
|
||||
key: 'global-update-password',
|
||||
component: UpdatePasswordContent,
|
||||
@@ -426,12 +488,22 @@ export const useDialogService = () => {
|
||||
})
|
||||
}
|
||||
|
||||
function showImportFailedNodeDialog(
|
||||
async function showImportFailedNodeDialog(
|
||||
options: {
|
||||
conflictedPackages?: ConflictDetectionResult[]
|
||||
dialogComponentProps?: DialogComponentProps
|
||||
} = {}
|
||||
) {
|
||||
const [
|
||||
{ default: ImportFailedNodeHeader },
|
||||
{ default: ImportFailedNodeFooter },
|
||||
{ default: ImportFailedNodeContent }
|
||||
] = await Promise.all([
|
||||
lazyImportFailedNodeHeader(),
|
||||
lazyImportFailedNodeFooter(),
|
||||
lazyImportFailedNodeContent()
|
||||
])
|
||||
|
||||
const { dialogComponentProps, conflictedPackages } = options
|
||||
|
||||
return dialogStore.showDialog({
|
||||
@@ -463,7 +535,7 @@ export const useDialogService = () => {
|
||||
})
|
||||
}
|
||||
|
||||
function showNodeConflictDialog(
|
||||
async function showNodeConflictDialog(
|
||||
options: {
|
||||
showAfterWhatsNew?: boolean
|
||||
conflictedPackages?: ConflictDetectionResult[]
|
||||
@@ -472,6 +544,16 @@ export const useDialogService = () => {
|
||||
onButtonClick?: () => void
|
||||
} = {}
|
||||
) {
|
||||
const [
|
||||
{ default: NodeConflictHeader },
|
||||
{ default: NodeConflictFooter },
|
||||
{ default: NodeConflictDialogContent }
|
||||
] = await Promise.all([
|
||||
lazyNodeConflictHeader(),
|
||||
lazyNodeConflictFooter(),
|
||||
lazyNodeConflictDialogContent()
|
||||
])
|
||||
|
||||
const {
|
||||
dialogComponentProps,
|
||||
buttonText,
|
||||
|
||||
Reference in New Issue
Block a user