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加载中显示的视图工厂函数
delaynumber200延迟显示 loading 的时间(毫秒),避免快速加载时的闪烁
timeoutnumber0超时时间(毫秒),<=0 不限制
onError(e: unknown) => View加载失败时的错误处理,返回一个视图作为替代

提示:如果不提供 loadinglazy() 会向上查找 Suspense 组件,让 Suspense 显示它的 fallbackloading 的优先级低于 Suspensefallback

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加载中显示的视图工厂函数
delaynumber延迟显示 loading 的时间,默认 200ms
timeoutnumber超时时间,默认 0(不限制)
onError(e: unknown) => View加载失败时的错误处理
propsWithProps<T>传递给加载完成组件的属性
childrenComponentProps<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>
  )
}

下一步