mirror of
https://github.com/MrXujiang/h5-Dooring.git
synced 2026-03-27 15:50:49 +00:00
Merge pull request #168 from GeekQiaQia/master
impove(dynamicEngine ): improve the dynamic engine performance
This commit is contained in:
commit
c6e9d3607a
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user