在大型应用中,合理的按需加载策略可以显著提升应用性能。本节将详细介绍如何在使用 Tailwind CSS 的项目中实现高效的按需加载。
样式按需加载
基础配置
// tailwind.config.js
module.exports = {
// 配置 JIT 模式
mode: 'jit',
// 精确配置需要处理的文件
content: [
'./src/pages/**/*.{js,jsx,ts,tsx}',
'./src/components/**/*.{js,jsx,ts,tsx}',
'./src/features/**/*.{js,jsx,ts,tsx}',
],
// 禁用未使用的功能
corePlugins: {
float: false,
clear: false,
objectFit: false,
},
}
动态样式加载
// utils/styleLoader.ts
export const loadStyles = async (stylePath: string) => {
try {
const styleModule = await import(
/* webpackChunkName: "styles/[request]" */
`../styles/${stylePath}.css`
);
return styleModule.default;
} catch (error) {
console.error('Failed to load styles:', error);
return null;
}
};
// 使用示例
const Component = () => {
useEffect(() => {
loadStyles('features/dashboard');
}, []);
return <div>Dashboard Component</div>;
};
组件按需加载
路由级别加载
// routes/index.tsx
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const Routes = () => (
<Suspense fallback={<LoadingSpinner />}>
<Switch>
<Route path="/dashboard">
<Dashboard />
</Route>
<Route path="/settings">
<Settings />
</Route>
</Switch>
</Suspense>
);
组件级别加载
// components/LazyComponent.tsx
import { lazy, Suspense } from 'react';
const DataTable = lazy(() => {
// 同时加载组件和样式
return Promise.all([
import('./DataTable'),
import('./DataTable.css')
]).then(([componentModule]) => componentModule);
});
const LazyDataTable = (props) => (
<Suspense
fallback={
<div className="animate-pulse bg-gray-200 h-64 rounded-lg" />
}
>
<DataTable {...props} />
</Suspense>
);
主题按需加载
主题配置
// styles/themes/index.ts
export const themes = {
light: () => import('./light.css'),
dark: () => import('./dark.css'),
custom: () => import('./custom.css'),
};
// hooks/useTheme.ts
import { useState, useEffect } from 'react';
import { themes } from '../styles/themes';
export const useTheme = (initialTheme = 'light') => {
const [theme, setTheme] = useState(initialTheme);
useEffect(() => {
const loadTheme = async () => {
const themeModule = await themes[theme]();
// 应用主题样式
};
loadTheme();
}, [theme]);
return { theme, setTheme };
};
图标按需加载
图标组件
// components/Icon/index.tsx
import { lazy, Suspense } from 'react';
const iconComponents = {
home: () => import('./icons/Home'),
settings: () => import('./icons/Settings'),
user: () => import('./icons/User'),
};
interface IconProps {
name: keyof typeof iconComponents;
size?: number;
className?: string;
}
const Icon: React.FC<IconProps> = ({ name, ...props }) => {
const LazyIcon = lazy(iconComponents[name]);
return (
<Suspense fallback={<div className="w-6 h-6 bg-gray-200 rounded" />}>
<LazyIcon {...props} />
</Suspense>
);
};
// 使用示例
const Header = () => (
<header>
<Icon name="home" className="w-6 h-6 text-gray-600" />
</header>
);
性能优化
预加载策略
// utils/preload.ts
const preloadComponent = (componentPath: string) => {
const component = import(
/* webpackPrefetch: true */
/* webpackChunkName: "[request]" */
`../components/${componentPath}`
);
return component;
};
// 路由配置中使用
const routes = [
{
path: '/dashboard',
component: lazy(() => preloadComponent('Dashboard')),
preload: () => preloadComponent('Dashboard'),
},
];
// 导航守卫中使用
const RouterGuard = ({ children }) => {
const location = useLocation();
useEffect(() => {
const route = routes.find(r => r.path === location.pathname);
if (route?.preload) {
route.preload();
}
}, [location]);
return children;
};
加载优化
// hooks/useAsyncComponent.ts
import { useState, useEffect } from 'react';
export const useAsyncComponent = (
loader: () => Promise<any>,
placeholder: React.ReactNode
) => {
const [Component, setComponent] = useState<React.ComponentType | null>(null);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
let mounted = true;
loader()
.then(module => {
if (mounted) {
setComponent(() => module.default);
}
})
.catch(err => {
if (mounted) {
setError(err);
console.error('Failed to load component:', err);
}
});
return () => {
mounted = false;
};
}, [loader]);
if (error) {
return <div>Error loading component</div>;
}
return Component ? <Component /> : placeholder;
};
// 使用示例
const AsyncFeature = () => {
return useAsyncComponent(
() => import('./Feature'),
<div className="animate-pulse bg-gray-200 h-32" />
);
};
缓存策略
模块缓存
// utils/moduleCache.ts
const moduleCache = new Map<string, any>();
export const loadCachedModule = async (key: string, loader: () => Promise<any>) => {
if (moduleCache.has(key)) {
return moduleCache.get(key);
}
const module = await loader();
moduleCache.set(key, module);
return module;
};
// 使用示例
const loadComponent = async (name: string) => {
return loadCachedModule(
`component:${name}`,
() => import(`../components/${name}`)
);
};
最佳实践
加载策略
- 配置精确的内容扫描
- 实现合理的预加载
- 使用适当的加载指示器
性能优化
- 实施代码分割
- 优化加载顺序
- 合理使用缓存
开发建议
- 模块化样式组织
- 组件粒度控制
- 合理使用占位符
监控分析
- 跟踪加载性能
- 分析资源使用
- 优化加载策略