Files
guilixu-admin/src/utils/lazy-load.ts
2026-05-31 12:09:14 +08:00

359 lines
8.2 KiB
TypeScript

/**
* 组件懒加载工具
*/
import { defineAsyncComponent, Component } from 'vue';
import { LoadingOutlined } from '@ant-design/icons-vue';
import { h } from 'vue';
// 加载状态组件
const LoadingComponent = {
setup() {
return () =>
h(
'div',
{
style: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '200px',
fontSize: '16px',
color: '#999'
}
},
[h(LoadingOutlined, { style: { marginRight: '8px' } }), '加载中...']
);
}
};
// 错误状态组件
const ErrorComponent = {
props: ['error'],
setup(props: { error: Error }) {
return () =>
h(
'div',
{
style: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
minHeight: '200px',
padding: '20px',
color: '#ff4d4f',
backgroundColor: '#fff2f0',
border: '1px solid #ffccc7',
borderRadius: '6px'
}
},
[
h(
'div',
{ style: { fontSize: '16px', marginBottom: '8px' } },
'组件加载失败'
),
h(
'div',
{ style: { fontSize: '12px', color: '#999' } },
props.error.message
),
h(
'button',
{
style: {
marginTop: '12px',
padding: '4px 12px',
border: '1px solid #d9d9d9',
borderRadius: '4px',
backgroundColor: '#fff',
cursor: 'pointer'
},
onClick: () => window.location.reload()
},
'重新加载'
)
]
);
}
};
// 懒加载配置选项
interface LazyLoadOptions {
loading?: Component;
error?: Component;
delay?: number;
timeout?: number;
retries?: number;
retryDelay?: number;
}
// 默认配置
const defaultOptions: LazyLoadOptions = {
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 30000,
retries: 3,
retryDelay: 1000
};
/**
* 创建懒加载组件
* @param loader 组件加载函数
* @param options 配置选项
*/
export function createLazyComponent(
loader: () => Promise<any>,
options: LazyLoadOptions = {}
) {
const config = { ...defaultOptions, ...options };
return defineAsyncComponent({
loader: createRetryLoader(loader, config.retries!, config.retryDelay!),
loadingComponent: config.loading,
errorComponent: config.error,
delay: config.delay,
timeout: config.timeout
});
}
/**
* 创建带重试机制的加载器
*/
function createRetryLoader(
loader: () => Promise<any>,
retries: number,
retryDelay: number
) {
return async () => {
let lastError: Error;
for (let i = 0; i <= retries; i++) {
try {
return await loader();
} catch (error) {
lastError = error as Error;
if (i < retries) {
await new Promise((resolve) => setTimeout(resolve, retryDelay));
console.warn(`组件加载失败,正在重试 (${i + 1}/${retries}):`, error);
}
}
}
throw lastError!;
};
}
/**
* 路由懒加载
*/
export function lazyRoute(
loader: () => Promise<any>,
options?: LazyLoadOptions
) {
return createLazyComponent(loader, {
...options,
loading: options?.loading || {
setup() {
return () =>
h(
'div',
{
style: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '60vh',
fontSize: '16px',
color: '#999'
}
},
[
h(LoadingOutlined, { style: { marginRight: '8px' } }),
'页面加载中...'
]
);
}
}
});
}
/**
* 模态框懒加载
*/
export function lazyModal(
loader: () => Promise<any>,
options?: LazyLoadOptions
) {
return createLazyComponent(loader, {
...options,
loading: options?.loading || {
setup() {
return () =>
h(
'div',
{
style: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '300px',
fontSize: '14px',
color: '#999'
}
},
[h(LoadingOutlined, { style: { marginRight: '8px' } }), '加载中...']
);
}
}
});
}
/**
* 图表懒加载
*/
export function lazyChart(
loader: () => Promise<any>,
options?: LazyLoadOptions
) {
return createLazyComponent(loader, {
...options,
loading: options?.loading || {
setup() {
return () =>
h(
'div',
{
style: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '400px',
backgroundColor: '#fafafa',
border: '1px dashed #d9d9d9',
borderRadius: '6px',
fontSize: '14px',
color: '#999'
}
},
[
h(LoadingOutlined, { style: { marginRight: '8px' } }),
'图表加载中...'
]
);
}
}
});
}
/**
* 预加载组件
*/
export class ComponentPreloader {
private preloadedComponents = new Map<string, Promise<any>>();
/**
* 预加载组件
*/
preload(key: string, loader: () => Promise<any>) {
if (!this.preloadedComponents.has(key)) {
this.preloadedComponents.set(key, loader());
}
return this.preloadedComponents.get(key)!;
}
/**
* 获取预加载的组件
*/
get(key: string) {
return this.preloadedComponents.get(key);
}
/**
* 清除预加载的组件
*/
clear(key?: string) {
if (key) {
this.preloadedComponents.delete(key);
} else {
this.preloadedComponents.clear();
}
}
/**
* 批量预加载
*/
batchPreload(components: Record<string, () => Promise<any>>) {
Object.entries(components).forEach(([key, loader]) => {
this.preload(key, loader);
});
}
}
// 全局预加载器实例
export const componentPreloader = new ComponentPreloader();
/**
* 智能懒加载 - 根据网络状况调整策略
*/
export function smartLazyComponent(
loader: () => Promise<any>,
options: LazyLoadOptions = {}
) {
// 检测网络状况
const connection = (navigator as any).connection;
const isSlowNetwork =
connection &&
(connection.effectiveType === 'slow-2g' ||
connection.effectiveType === '2g' ||
connection.saveData);
// 根据网络状况调整配置
const smartOptions = {
...options,
timeout: isSlowNetwork ? 60000 : options.timeout || 30000,
retries: isSlowNetwork ? 5 : options.retries || 3,
retryDelay: isSlowNetwork ? 2000 : options.retryDelay || 1000
};
return createLazyComponent(loader, smartOptions);
}
/**
* 可见性懒加载 - 只有当组件进入视口时才加载
*/
export function visibilityLazyComponent(
loader: () => Promise<any>,
options: LazyLoadOptions = {}
) {
return defineAsyncComponent({
loader: () => {
return new Promise((resolve, reject) => {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
observer.disconnect();
loader().then(resolve).catch(reject);
}
});
// 创建一个占位元素来观察
const placeholder = document.createElement('div');
document.body.appendChild(placeholder);
observer.observe(placeholder);
// 清理函数
setTimeout(() => {
observer.disconnect();
document.body.removeChild(placeholder);
reject(new Error('Visibility timeout'));
}, options.timeout || 30000);
});
},
loadingComponent: options.loading || LoadingComponent,
errorComponent: options.error || ErrorComponent,
delay: options.delay || 200
});
}