延迟加载组件
lazy 如何延迟获取组件代码?
- 加载契约:
lazy(load)返回可渲染组件,首次尝试渲染时才调用 load,等待 Promise 解析为具有 default 组件导出的对象; - 缓存行为: React 缓存加载 Promise 和解析结果,不会每次重新渲染都重复调用加载函数;
- 配合边界: 外层 Suspense 表达加载等待,加载失败交给错误边界;
import { lazy, Suspense } from "react";
const Editor = lazy(() => import("./Editor.js"));
export default function Page() {
return (
<Suspense fallback={<p>正在加载编辑器……</p>}>
<Editor />
</Suspense>
);
}
为什么 lazy 声明应该放在模块顶层?
- 稳定类型: 在组件体中重新调用 lazy 会反复创建新的组件类型,可能导致状态重置;
- 按需显示: 顶层声明不会立即下载代码,是否渲染该组件才决定何时触发加载;
- 条件边界: 可在 JSX 中按条件渲染延迟组件,不需要把声明移进条件或事件;
如何排查加载结果无法渲染?
- 导出核对: 动态 import 的模块通常需要 default 导出,只有命名导出时显式转换为
{ default: module.Editor }; - Promise 失败: 检查网络、部署后的资源路径和代码分块,不把失败永远显示成加载中;
- 资源用途: lazy 专门包装组件代码,数据 Promise 读取使用 use,普通事件中的动态模块加载可直接 import;
const NamedEditor = lazy(() =>
import("./Editor.js").then((module) => ({
default: module.Editor,
})),
);
怎样选择合理的代码拆分粒度?
- 用户路径: 页面、大型编辑器或不常打开的面板常是合适边界,细碎拆分可能增加请求与加载占位;
- 框架协调: 路由框架通常能把代码与数据一起预加载,优先利用已有边界,再用 lazy 补充局部场景;