From 1e4faf3d69643b6539fe7c307480002a896f7527 Mon Sep 17 00:00:00 2001 From: GeekQiaQia Date: Sun, 22 Mar 2026 14:31:03 +0800 Subject: [PATCH] impove(dynamicEngine ): improve the dynamic engine performance --- src/core/DynamicEngine.tsx | 170 +++++++++++++++++++++++++++++-------- 1 file changed, 134 insertions(+), 36 deletions(-) diff --git a/src/core/DynamicEngine.tsx b/src/core/DynamicEngine.tsx index c95d7d0..c2d5e1a 100644 --- a/src/core/DynamicEngine.tsx +++ b/src/core/DynamicEngine.tsx @@ -1,45 +1,143 @@ -import { dynamic } from "umi"; +import React, { memo, FC, lazy, Suspense, Component, ErrorInfo } from "react"; import Loading from "../components/LoadingCp"; -import { useMemo, memo, FC } from "react"; -import React from "react"; -export type componentsType = "media" | "base" | "visible" | "shop"; +// ============================================================================ +// Error Boundary for React 16 +// ============================================================================ +interface ErrorBoundaryProps { + fallbackRender: (props: { error: Error }) => React.ReactNode; + children: React.ReactNode; +} -const DynamicFunc = (type: string, componentsType: string) => { - return dynamic({ - loader: async function() { - const { default: Graph } = await import( - `@/materials/${componentsType}/${type}` - ); - const Component = Graph; - return (props: DynamicType) => { - const { config, isTpl } = props; - return ; - }; - }, - loading: () => ( -
- -
- ) - }); -}; +interface ErrorBoundaryState { + hasError: boolean; + error?: Error; +} -type DynamicType = { +class ErrorBoundary extends Component { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error("Component error:", error, errorInfo); + } + + render() { + if (this.state.hasError && this.state.error) { + return this.props.fallbackRender({ error: this.state.error }); + } + return this.props.children; + } +} + +// ============================================================================ +// Type Definitions - More Strict & Predictive +// ============================================================================ +export type ComponentsType = "media" | "base" | "visible" | "shop"; + +export interface DynamicComponentProps { isTpl: boolean; - config: { [key: string]: any }; + config: Record; type: string; - componentsType: componentsType; - category: string; -}; -const DynamicEngine = memo((props: DynamicType) => { - const { type, config, category } = props; - const Dynamic = useMemo(() => { - return (DynamicFunc(type, category) as unknown) as FC; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [config]); + category: ComponentsType; +} - return ; -}); +// ============================================================================ +// Component Registry Pattern - Cache Loaded Components +// ============================================================================ +type LazyComponent = ReturnType; +const componentCache = new Map(); + +/** + * Get or create lazy-loaded component with caching + * Prevents redundant dynamic component creation + */ +const getLazyComponent = (type: string, category: ComponentsType) => { + const key = `${category}/${type}`; + + if (!componentCache.has(key)) { + const lazyComponent = lazy(() => + import(`@/materials/${category}/${type}`).catch(error => { + console.error(`Failed to load component: ${key}`, error); + // Return fallback component + return import(`@/materials/base/Text`); + }) + ); + componentCache.set(key, lazyComponent); + } + + return componentCache.get(key)!; +}; + +// ============================================================================ +// Preload Function - Proactive Component Loading +// ============================================================================ +/** + * Preload components before they're needed (e.g., on editor mount) + */ +export const preloadComponents = async ( + components: Array<{ type: string; category: ComponentsType }> +) => { + const promises = components.map(({ type, category }) => + import(`@/materials/${category}/${type}`) + ); + await Promise.all(promises); +}; + +// ============================================================================ +// Optimized DynamicEngine +// ============================================================================ +const DynamicEngine: FC = memo( + ({ type, config, category, isTpl }) => { + // Get cached lazy component + const LazyComponent = getLazyComponent(type, category); + + return ( + + + + } + > + ( + + )} + > + + + + ); + } +); + +// ============================================================================ +// Error Display Component +// ============================================================================ +interface ComponentErrorProps { + type: string; + error?: Error; +} + +const ComponentError = ({ type, error }: ComponentErrorProps) => ( +
+
Failed to load component: {type}
+ {error &&
{error.message}
} +
+); export default DynamicEngine;