组件懒加载核心是手动控制 import() 触发时机,如绑定 onclick;需用 usestate 管理状态,避免提前声明 react.lazy;可封装为 useloadable hook 统一处理加载逻辑;suspense 不适用于显式点击触发;hover 预加载可提升体验。

组件懒加载按需触发,核心是把 import() 动态导入和用户交互(比如点击)绑定起来,而不是在组件定义或挂载时就发起请求。关键不在“懒”,而在“谁来触发”——你得手动控制加载时机。
用 onClick 触发动态 import
最直接的方式:把 import() 放进按钮的点击事件里,配合 useState 和 React.lazy 实现按需加载与渲染。
- 不提前声明 lazy 组件,避免初始化时加载
- 点击后调用
import(),拿到模块再用React.createElement渲染 - 可用
useState管理加载状态(pending / loaded / error)
function MyPage() {
const [Comp, setComp] = useState(null);
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
try {
const module = await import('./HeavyComponent');
setComp(() => module.default);
} catch (err) {
console.error('加载失败', err);
} finally {
setLoading(false);
}
};
return (
<div>
<button onclick="{handleClick}" disabled>
{loading ? '加载中...' : '加载重型组件'}
</button>
{Comp && <comp></comp>}
</div>
);
}
封装成可复用的 useLoadable Hook
把加载逻辑抽离成自定义 Hook,支持任意组件、自动处理 loading/error 状态,更贴近实际工程需求。
- 接收一个返回
Promise的工厂函数(如() => import('./X')) - 返回
{ load, Component, isLoading, error }等状态和方法 - 组件内只调用
load(),无需关心 Promise 链和 setState 细节
function useLoadable(factory) {
const [Component, setComponent] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const load = useCallback(async () => {
setIsLoading(true);
setError(null);
try {
const mod = await factory();
setComponent(() => mod.default);
} catch (err) {
setError(err);
} finally {
setIsLoading(false);
}
}, [factory]);
return { load, Component, isLoading, error };
}
// 使用
function Page() {
const { load, Component, isLoading } = useLoadable(() => import('./Chart'));
return (
<div>
<button onclick="{load}" disabled>
{isLoading ? '图表加载中' : '显示图表'}
</button>
{Component && <component></component>}
</div>
);
}
配合 Suspense?注意它的限制
React.Suspense 默认只对 React.lazy 生效,且触发时机由 React 控制(如首次渲染、状态更新),**无法响应按钮点击这类显式动作**。若强行用它做“点击加载”,容易误入歧途:
- 不能把
lazy(() => import(...))放在点击回调里——它只是个函数,不会自动触发加载 - 如果用
useState切换一个 lazy 组件变量,Suspense 才会捕获,但本质仍是靠状态驱动,不是点击直接触发 - 真正想“点一下才开始下载”,还是推荐上面的手动
import()+ 状态管理方式,更可控、无黑盒
进阶:预加载但不立即执行
有时希望“用户 hover 按钮时预加载,点击时直接渲染”,提升响应速度。可用 import() 配合 Promise 缓存实现:
- hover 时调用
import()并缓存 Promise(避免重复请求) - 点击时 await 这个已缓存的 Promise,几乎瞬时 resolve
- 适合有明确操作路径(如菜单项 hover 后大概率点击)的场景
let preloadPromise = null;
function handleHover() {
if (!preloadPromise) {
preloadPromise = import('./ExpensiveModal');
}
}
function handleClick() {
preloadPromise?.then(mod => {
// 渲染 mod.default
});
}
不复杂但容易忽略










