React.lazy()原理
React.lazy()
是 React 提供用于代码分割 (code splitting) 的一个核心功能。它允许我们延迟加载组件的代码,直到该组件首次需要被渲染时才进行加载,这有助于优化应用的初始加载时间和性能。其实现主要依赖于 JavaScript 的动态 import()
语法和 React 的 Suspense
组件。
其核心工作流程可以概括为以下几个步骤:
-
创建 Lazy 组件封装:
- 当你调用
React.lazy(() => import('./MyComponent'))
时,React 并不会立即执行这个导入函数。 - 它会返回一个特殊的 React 组件类型(我们可以称之为“Lazy 组件”或“包装组件”)。这个包装组件内部保存了执行动态导入的函数引用。
- 当你调用
-
首次渲染与触发加载:
- 当 React 首次尝试渲染这个“Lazy 组件”时,它会检查其包裹的真实组件代码是否已经加载。
- 初始状态下,代码未加载,于是“Lazy 组件”会调用我们传递给它的动态
import()
函数。 - 这个
import()
函数返回一个 Promise,表示组件代码正在异步加载中。
-
抛出 Promise (核心机制):
- 在组件代码加载完成之前,“Lazy 组件”在渲染时并不会返回任何实际的 UI 结构,而是会抛出 (throw) 这个由动态
import()
返回的 Promise。
- 在组件代码加载完成之前,“Lazy 组件”在渲染时并不会返回任何实际的 UI 结构,而是会抛出 (throw) 这个由动态
-
React.Suspense
捕获与处理:- 这个被抛出的 Promise 会被其最近的父级
<React.Suspense fallback={...}>
组件捕获。 Suspense
组件捕获到 Promise 后,会:- 显示在其
fallback
属性中指定的加载指示器 UI(例如一个加载动画或提示文字)。 - 挂起 (suspend) 当前的渲染流程,并监听这个 Promise 的状态。
- 显示在其
- 这个被抛出的 Promise 会被其最近的父级
-
Promise 完成与组件实际渲染:
- 当动态
import()
的 Promise resolve 时(意味着组件代码已成功加载并解析):Suspense
组件会收到通知。- React 会尝试重新渲染之前被挂起的“Lazy 组件”。
- 这一次,“Lazy 组件”再次渲染时,它会从已 resolve 的 Promise 的结果(即加载的模块对象)中获取到真实的 React 组件(通常是模块的
default
导出)。 - 然后,“Lazy 组件”才会渲染出那个实际加载完成的组件,替换掉
Suspense
的fallback
UI。
- 当动态
-
错误处理:
- 如果动态
import()
的 Promise reject(即组件代码加载失败):- “Lazy 组件”会抛出一个错误。
- 这个错误可以被最近的 React 错误边界 (Error Boundary) 组件捕获,从而进行优雅的错误处理,而不是导致整个应用崩溃。
- 如果动态
总结来说:
React.lazy()
的底层原理可以视为一种“渲染时请求数据(这里是组件代码)”的模式。它通过让组件在未就绪时抛出 Promise,并由 Suspense
组件捕获和处理这种“挂起”状态,实现了组件的按需加载和优雅的加载状态展示。这种机制是 React 并发模式 (Concurrent Mode) 中处理异步操作的核心思想之一。