Lazy 与 lazy
Vitarx 提供了两种方式来实现组件的懒加载:Lazy 组件和 lazy() 工厂函数。它们都可以延迟加载组件代码,只在真正需要渲染时才发起请求,从而减小首屏包体积。
lazy() 工厂函数(推荐)
lazy() 是创建懒加载组件的推荐方式。它返回一个可以直接在 JSX 中使用的组件构建器,用法和普通组件几乎一样:
tsx
import { lazy } from 'vitarx'
// 创建懒加载组件
const AsyncChart = lazy(() => import('./Chart'))
function App() {
return (
<div>
<h1>数据面板</h1>
{/* 像普通组件一样使用,属性会自动透传 */}
<AsyncChart title="销售趋势" />
</div>
)
}配置选项
lazy() 的第二个参数可以配置加载行为:
tsx
import { lazy } from 'vitarx'
const AsyncComp = lazy(() => import('./HeavyComponent'), {
// 延迟 300ms 后才显示 loading 视图,避免闪烁
delay: 300,
// 超时 10 秒
timeout: 10000,
// 加载中显示的视图
loading: () => <div>加载中...</div>,
// 加载失败时显示的视图
onError: (e) => <div>加载失败,请刷新重试</div>
})| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
loading | () => View | — | 加载中显示的视图工厂函数 |
delay | number | 200 | 延迟显示 loading 的时间(毫秒),避免快速加载时的闪烁 |
timeout | number | 0 | 超时时间(毫秒),<=0 不限制 |
onError | (e: unknown) => View | — | 加载失败时的错误处理,返回一个视图作为替代 |
提示:如果不提供
loading,lazy()会向上查找Suspense组件,让Suspense显示它的fallback。loading的优先级低于Suspense的fallback。
Lazy 组件
Lazy 组件是另一种懒加载方式,通过 loader 属性传入加载函数,通过 children 渲染函数接收加载完成的组件:
tsx
import { Lazy } from 'vitarx'
function App() {
return (
<Lazy
loader={() => import('./HeavyComponent')}
loading={() => <div>加载中...</div>}
timeout={10000}
onError={(e) => <div>加载失败</div>}
>
{(Component) => <Component title="hello" />}
</Lazy>
)
}Lazy 组件的属性:
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
loader | () => Promise<{ default: T }> | 是 | 懒加载器,返回动态 import 的 Promise |
loading | () => View | 否 | 加载中显示的视图工厂函数 |
delay | number | 否 | 延迟显示 loading 的时间,默认 200ms |
timeout | number | 否 | 超时时间,默认 0(不限制) |
onError | (e: unknown) => View | 否 | 加载失败时的错误处理 |
props | WithProps<T> | 否 | 传递给加载完成组件的属性 |
children | ComponentProps<T>['children'] | 否 | 透传给加载完成后的组件 |
推荐使用
lazy()工厂函数,因为它的用法更简洁,可以直接像普通组件一样使用,不需要额外的渲染函数包装。
preloadComponent() — 预加载
如果你想在某个时机提前加载组件(比如用户 hover 到某个链接时),可以使用 preloadComponent():
tsx
import { lazy, preloadComponent } from 'vitarx'
const AsyncChart = lazy(() => import('./Chart'))
function App() {
// 鼠标移入时预加载
const handleMouseEnter = () => {
preloadComponent(() => import('./Chart'))
}
return (
<div>
<button
onMouseEnter={handleMouseEnter}
onClick={() => {
/* 跳转到图表页 */
}}
>
查看图表
</button>
</div>
)
}preloadComponent() 返回一个 Promise,加载成功后组件会被缓存,后续使用时直接从缓存获取。
getCachedComponent() — 获取已缓存组件
检查某个懒加载组件是否已经加载并缓存:
tsx
import { getCachedComponent } from 'vitarx'
const loader = () => import('./Chart')
// 检查是否已缓存
const cached = getCachedComponent(loader)
if (cached) {
console.log('组件已缓存,可以直接使用')
}与 Suspense 配合使用
当 lazy() 没有配置 loading 时,它会自动向上查找 Suspense 组件,让 Suspense 显示 fallback:
tsx
import { Suspense, lazy } from 'vitarx'
// 没有配置 loading,会使用 Suspense 的 fallback
const AsyncTable = lazy(() => import('./Table'))
const AsyncChart = lazy(() => import('./Chart'))
function Dashboard() {
return (
<Suspense fallback={<div>数据加载中...</div>}>
<AsyncTable />
<AsyncChart />
</Suspense>
)
}完整示例
下面是一个完整的懒加载示例,包含 lazy()、Suspense、预加载和错误处理:
tsx
import { lazy, Suspense, preloadComponent, ref } from 'vitarx'
// 懒加载首页组件
const Home = lazy(() => import('./Home'), {
loading: () => <div>首页加载中...</div>,
delay: 200
})
// 懒加载关于页组件,带超时和错误处理
const About = lazy(() => import('./About'), {
loading: () => <div>关于页加载中...</div>,
delay: 200,
timeout: 10000,
onError: (e) => (
<div>
<p>加载失败,请重试</p>
<button onClick={() => location.reload()}>刷新页面</button>
</div>
)
})
// 懒加载设置页组件,不配置 loading,使用 Suspense 的 fallback
const Settings = lazy(() => import('./Settings'))
function App() {
const currentPage = ref('home')
/** 切换页面时预加载下一个页面 */
const switchPage = (page: string) => {
currentPage.value = page
// 预加载其他页面
if (page === 'home') preloadComponent(() => import('./About'))
}
return (
<div>
<nav>
<button onClick={() => switchPage('home')}>首页</button>
<button onClick={() => switchPage('about')}>关于</button>
<button onClick={() => switchPage('settings')}>设置</button>
</nav>
<main>
{currentPage.value === 'home' && <Home />}
{currentPage.value === 'about' && <About />}
{currentPage.value === 'settings' && (
// Settings 没有配置 loading,使用 Suspense 的 fallback
<Suspense fallback={<div>设置页加载中...</div>}>
<Settings />
</Suspense>
)}
</main>
</div>
)
}