React第二十七章(Suspense)

Suspense

Suspense 是一种异步渲染机制,其核心理念是在组件加载或数据获取过程中,先展示一个占位符(loading state),从而实现更自然流畅的用户界面更新体验。

应用场景

  • 异步组件加载:通过代码分包实现组件的按需加载,有效减少首屏加载时的资源体积,提升应用性能。

  • 异步数据加载:在数据请求过程中展示优雅的过渡状态(如 loading 动画、骨架屏等),为用户提供更流畅的交互体验。

  • 异步图片资源加载:智能管理图片资源的加载状态,在图片完全加载前显示占位内容,确保页面布局稳定,提升用户体验。

用法

ts 复制代码
<Suspense fallback={<div>Loading...</div>}>
  <AsyncComponent />
</Suspense>

入参:

  • fallback: 指定在组件加载或数据获取过程中展示的组件或元素
  • children: 指定要异步加载的组件或数据

案例

异步组件加载

创建一个异步组件

  • src/components/Async/index.tsx
ts 复制代码
export const AsyncComponent = () => {
    return <div>Async</div>
}

export default AsyncComponent
  • src/App.tsx

使用lazy进行异步加载组件,使用Suspense包裹异步组件,fallback指定加载过程中的占位组件

ts 复制代码
import React, { useRef, useState, Suspense,lazy } from 'react';
const AsyncComponent = lazy(() => import('./components/Async'))
const App: React.FC = () => {

  return (
    <>
      <Suspense fallback={<div>loading</div>}>
        <AsyncComponent />
      </Suspense>
    </>
  );
}

export default App;

效果如下:

可以将网络调整到慢速,可以看到loading效果


异步数据加载

我们实现卡片详情,在数据加载过程中展示骨架屏,数据加载完成后展示卡片详情。

建议升级到React19, 因为我们会用到一个use的API, 这个API在React18中是实验性特性,在React19纳入正式特性

模拟数据,我们放到public目录下, 方便获取直接(通过地址 + 文件名获取) 例如:

http://localhost:5173/data.json

  • public/data.json
json 复制代码
{
    "data":{
        "id":1,
        "address":"北京市房山区住岗子村10086",
        "name":"小满",
        "age":26,
        "avatar":"https://api.dicebear.com/7.x/avataaars/svg?seed=小满"
    }
}   

创建一个骨架屏组件,用于在数据加载过程中展示,提升用户体验,当然你封装loading组件也是可以的。

  • src/components/skeleton/index.tsx
ts 复制代码
import './index.css'
export const Skeleton = () => {
    return <div className="skeleton">
        <header className="skeleton-header">
            <div className="skeleton-name"></div>
            <div className="skeleton-age"></div>
        </header>
        <section className="skeleton-content">
            <div className="skeleton-address"></div>
            <div className="skeleton-avatar"></div>
        </section>
    </div>
}
css 复制代码
.skeleton {
    width: 300px;
    height: 150px;
    border: 1px solid #d6d3d3;
    margin: 30px;
    border-radius: 2px;
}

.skeleton-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid #d6d3d3;
    padding: 10px;
}

.skeleton-name {
    width: 100px;
    height: 20px;
    background-color: #d6d3d3;
    animation: skeleton-loading 1.5s ease-in-out infinite;
}

.skeleton-age {
    width: 50px;
    height: 20px;
    background-color: #d6d3d3;
    animation: skeleton-loading 1.5s ease-in-out infinite;
}

.skeleton-content {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px;
}

.skeleton-address {
    width: 100px;
    height: 20px;
    background-color: #d6d3d3;
    animation: skeleton-loading 1.5s ease-in-out infinite;
}

.skeleton-avatar {
    width: 50px;
    height: 50px;
    background-color: #d6d3d3;
    animation: skeleton-loading 1.5s ease-in-out infinite;
}

@keyframes skeleton-loading {
    0% {
        opacity: 0.6;
    }
    50% {
        opacity: 1;
    }
    100% {
        opacity: 0.6;
    }
}

创建一个卡片组件,用于展示数据,这里面介绍一个新的API use

use API 用于获取组件内部的Promise,或者Context的内容,该案例使用了use获取Promise返回的数据并且故意延迟2秒返回,模拟网络请求。

  • src/components/Card/index.tsx
ts 复制代码
import { use } from 'react'
import './index.css'
interface Data {
   name: string
   age: number
   address: string
   avatar: string
}

const getData = async () => {
   await new Promise(resolve => setTimeout(resolve, 2000))
   return await fetch('http://localhost:5173/data.json').then(res => res.json()) as { data: Data }
};

const dataPromise = getData();

const Card: React.FC = () => {
   const { data } = use(dataPromise);
   return <div className="card">
      <header className="card-header">
         <div className="card-name">{data.name}</div>
         <div className="card-age">{data.age}</div>
      </header>
      <section className="card-content">
         <div className="card-address">{data.address}</div>
         <div className="card-avatar">
            <img width={50} height={50} src={data.avatar} alt="" />
         </div>
      </section>
   </div>;
};

export default Card;
css 复制代码
.card {
    width: 300px;
    height: 150px;
    border: 1px solid #d6d3d3;
    margin: 30px;
    border-radius: 2px;
}

.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid #d6d3d3;
    padding: 10px;
}

.card-age {
    font-size: 12px;
    color: #999;
}

.card-content {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px;
}

使用方式如下: 通过Suspense包裹Card组件,fallback指定骨架屏组件

  • src/App.tsx
ts 复制代码
import React, { useRef, useState, Suspense,lazy } from 'react';
import Card from './components/Card'
import { Skeleton } from './components/Skeleton'
const App: React.FC = () => {

  return (
    <>
      <Suspense fallback={<Skeleton />}>
        <Card />
      </Suspense>
    </>
  );
}

export default App;

效果如下:

相关推荐
2501_9209317018 分钟前
React Native鸿蒙跨平台实现推箱子游戏,完成玩家移动与箱子推动,当所有箱子都被推到目标位置时,玩家获胜
javascript·react native·react.js·游戏·ecmascript·harmonyos
layman05281 小时前
webpack5 css-loader:从基础到原理
前端·css·webpack
半桔1 小时前
【前端小站】CSS 样式美学:从基础语法到界面精筑的实战宝典
前端·css·html
AI老李1 小时前
PostCSS完全指南:功能/配置/插件/SourceMap/AST/插件开发/自定义语法
前端·javascript·postcss
_OP_CHEN1 小时前
【前端开发之CSS】(一)初识 CSS:网页化妆术的终极指南,新手也能轻松拿捏页面美化!
前端·css·html·网页开发·样式表·界面美化
啊哈一半醒1 小时前
CSS 主流布局
前端·css·css布局·标准流 浮动 定位·flex grid 响应式布局
PHP武器库1 小时前
ULUI:不止于按钮和菜单,一个专注于“业务组件”的纯 CSS 框架
前端·css
方也_arkling1 小时前
Element Plus主题色定制
javascript·sass
电商API_180079052471 小时前
第三方淘宝商品详情 API 全维度调用指南:从技术对接到生产落地
java·大数据·前端·数据库·人工智能·网络爬虫
晓晓莺歌1 小时前
vue3某一个路由切换,导致所有路由页面均变成空白页
前端·vue.js