Merge pull request #168 from GeekQiaQia/master

impove(dynamicEngine ): improve the dynamic engine performance
This commit is contained in:
徐小夕 2026-03-22 22:29:13 +08:00 committed by GitHub
commit c6e9d3607a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,45 +1,143 @@
import { dynamic } from "umi"; import React, { memo, FC, lazy, Suspense, Component, ErrorInfo } from "react";
import Loading from "../components/LoadingCp"; 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) => { interface ErrorBoundaryState {
return dynamic({ hasError: boolean;
loader: async function() { error?: Error;
const { default: Graph } = await import( }
`@/materials/${componentsType}/${type}`
);
const Component = Graph;
return (props: DynamicType) => {
const { config, isTpl } = props;
return <Component {...config} isTpl={isTpl} />;
};
},
loading: () => (
<div style={{ paddingTop: 10, textAlign: "center" }}>
<Loading />
</div>
)
});
};
type DynamicType = { class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
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; isTpl: boolean;
config: { [key: string]: any }; config: Record<string, unknown>;
type: string; type: string;
componentsType: componentsType; category: ComponentsType;
category: string; }
};
const DynamicEngine = memo((props: DynamicType) => {
const { type, config, category } = props;
const Dynamic = useMemo(() => {
return (DynamicFunc(type, category) as unknown) as FC<DynamicType>;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [config]);
return <Dynamic {...props} />; // ============================================================================
}); // Component Registry Pattern - Cache Loaded Components
// ============================================================================
type LazyComponent = ReturnType<typeof lazy>;
const componentCache = new Map<string, LazyComponent>();
/**
* 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<DynamicComponentProps> = memo(
({ type, config, category, isTpl }) => {
// Get cached lazy component
const LazyComponent = getLazyComponent(type, category);
return (
<Suspense
fallback={
<div style={{ paddingTop: 10, textAlign: "center" }}>
<Loading />
</div>
}
>
<ErrorBoundary
fallbackRender={({ error }) => (
<ComponentError type={type} error={error} />
)}
>
<LazyComponent {...config} isTpl={isTpl} />
</ErrorBoundary>
</Suspense>
);
}
);
// ============================================================================
// Error Display Component
// ============================================================================
interface ComponentErrorProps {
type: string;
error?: Error;
}
const ComponentError = ({ type, error }: ComponentErrorProps) => (
<div
style={{
padding: 20,
textAlign: "center",
color: "#ff4d4f",
border: "1px dashed #ff4d4f"
}}
>
<div>Failed to load component: {type}</div>
{error && <div style={{ fontSize: 12, marginTop: 8 }}>{error.message}</div>}
</div>
);
export default DynamicEngine; export default DynamicEngine;