005-React性能优化

React 性能优化深度指南

除了使用 Performance 工具分析性能瓶颈外,React 应用还有多种高效的优化策略。下面从组件设计、渲染控制、资源管理等维度全面解析性能优化方案:

一、组件级优化策略

1. 渲染控制技术

技术 实现方式 适用场景
React.memo const MemoComp = React.memo(MyComp) 纯展示组件,Props 不变时不重渲染
useMemo const data = useMemo(() => transform(rawData), [rawData]) 复杂计算缓存,避免重复执行
useCallback const handler = useCallback(() => {...}, [deps]) 防止回调函数重复创建导致子组件重渲染
PureComponent class MyComp extends React.PureComponent Class 组件浅比较优化
shouldComponentUpdate shouldComponentUpdate(nextProps, nextState) Class 组件细粒度更新控制

useMemo 与 useCallback 最佳实践:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Parent() {
const [count, setCount] = useState(0);

// 避免每次渲染创建新函数
const increment = useCallback(() => setCount(c => c + 1), []);

// 避免每次渲染重新计算
const formattedCount = useMemo(() => `Count: ${count}`, [count]);

return (
<div>
<Child onClick={increment} text={formattedCount} />
<ExpensiveComponent data={heavyData} />
</div>
);
}

const Child = React.memo(({ onClick, text }) => (
<button onClick={onClick}>{text}</button>
));

2. 组件分割原则

  • 按功能拆分:将大型组件拆分为多个小型专用组件
  • 提取高频更新部分:隔离状态变化频繁的 UI 区域
  • 延迟加载边界:在组件树中设置 React.lazy 分割点
1
2
3
4
5
6
7
8
9
10
// 组件分割示例
function UserDashboard() {
return (
<div>
<UserProfile /> {/* 静态内容 */}
<UserStatistics /> {/* 数据驱动,频繁更新 */}
<RecentActivity /> {/* 独立更新区域 */}
</div>
);
}

二、状态管理优化

1. 状态提升与下沉策略

策略 描述 优化效果
状态提升 将状态移动到共同父组件 减少跨组件通信
状态下沉 将状态移动到使用位置 避免无关组件重渲染
状态隔离 使用 Context + useMemo 精准控制消费者更新

2. 高效状态更新模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ✅ 函数式更新避免依赖旧状态
setCount(prev => prev + 1);

// ✅ 批量状态更新
const updateValues = () => {
setValueA(1);
setValueB(2);
// React 18+ 自动批处理
};

// ❌ 避免在循环中连续setState
data.forEach(item => {
setList(prev => [...prev, item]); // 低效
});

// ✅ 使用临时变量批量更新
const newList = [];
data.forEach(item => newList.push(item));
setList(newList);

3. Context 优化方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建分拆的 Context
const UserContext = React.createContext();
const SettingsContext = React.createContext();

// 优化消费组件
const UserProfile = () => {
// 只订阅需要的 Context
const user = useContext(UserContext);
return <div>{user.name}</div>;
};

// 使用记忆化 Provider 值
function App() {
const [user, setUser] = useState(null);

// ✅ 避免每次渲染创建新对象
const userValue = useMemo(() => ({ user, setUser }), [user]);

return (
<UserContext.Provider value={userValue}>
<MainLayout />
</UserContext.Provider>
);
}

三、渲染性能提升

1. 虚拟化长列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);

const VirtualList = () => (
<List
height={600}
itemCount={1000}
itemSize={35}
width={300}
>
{Row}
</List>
);

2. 时间分片策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用 requestIdleCallback 分割任务
const processChunk = (start) => {
const end = Math.min(start + 100, largeDataSet.length);

for (let i = start; i < end; i++) {
// 处理数据块
}

if (end < largeDataSet.length) {
requestIdleCallback(() => processChunk(end));
}
};

// 初始调用
requestIdleCallback(() => processChunk(0));

3. 动画优化技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 使用 CSS transform 代替 top/left
.animated-item {
transition: transform 0.3s ease;
will-change: transform; /* 提示浏览器优化 */
}

// React 动画库推荐
import { useSpring, animated } from '@react-spring/web';

function FadeIn() {
const props = useSpring({
opacity: 1,
from: { opacity: 0 },
config: { tension: 300, friction: 20 }
});

return <animated.div style={props}>Fades in</animated.div>;
}

四、资源加载优化

1. 代码分割策略

技术 实现方式 效果
路由级分割 const Home = React.lazy(() => import('./Home')) 按路由加载
组件级分割 const Map = React.lazy(() => import('./Map')) 按需加载重组件
预加载 <link rel="preload" href="module.js" as="script"> 提前加载关键资源
1
2
3
4
5
6
7
8
9
10
11
12
// 带加载状态的代码分割
const ChatWidget = React.lazy(() => import('./ChatWidget'));

function App() {
return (
<div>
<Suspense fallback={<Spinner />}>
<ChatWidget />
</Suspense>
</div>
);
}

2. 资源优化技术

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 图片优化
<img
src="image.jpg"
srcSet="image-480w.jpg 480w, image-800w.jpg 800w"
sizes="(max-width: 600px) 480px, 800px"
alt="Responsive"
loading="lazy"
/>

// 字体优化
<style>
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* 避免阻塞渲染 */
}
</style>

五、构建与运行时优化

1. 构建工具配置

1
2
3
4
5
6
7
8
9
10
11
12
13
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
minSize: 30000, // 30KB 以上拆包
},
runtimeChunk: 'single', // 分离运行时
},
plugins: [
new BundleAnalyzerPlugin(), // 分析包大小
]
};

2. 服务端渲染优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Next.js 优化示例
export async function getServerSideProps() {
const data = await fetchData();

return {
props: { data },
// 增量静态再生 (ISR)
revalidate: 60, // 每60秒重新生成
};
}

// 流式渲染
import { renderToPipeableStream } from 'react-dom/server';

const stream = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
res.setHeader('Content-type', 'text/html');
stream.pipe(res);
}
});

六、React 18+ 新特性优化

1. 并发模式特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 使用 useTransition 管理耗时操作
function SearchBox() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();

const handleChange = (e) => {
const value = e.target.value;
setQuery(value);

// ✅ 标记非紧急更新
startTransition(() => {
fetchResults(value).then(setResults);
});
};

return (
<div>
<input value={query} onChange={handleChange} />
{isPending ? <Spinner /> : <Results list={results} />}
</div>
);
}

2. 服务端 Suspense 流式渲染

1
2
3
4
5
6
7
8
9
10
// 服务端组件 (Next.js 13+)
async function UserProfile({ userId }) {
const user = await fetchUser(userId);
return <Profile user={user} />;
}

// 客户端使用
<Suspense fallback={<ProfileSkeleton />}>
<UserProfile userId={123} />
</Suspense>

七、性能优化检查清单

  1. 渲染优化

    • 使用 React.memo/PureComponent 避免无效渲染
    • 正确使用 useMemo/useCallback 缓存
    • 确保依赖数组完整且准确
  2. 状态管理

    • 避免在渲染中直接修改状态
    • 使用批量更新减少渲染次数
    • 复杂状态使用 useReducer 替代 useState
  3. 资源加载

    • 实现路由级和组件级代码分割
    • 图片资源使用懒加载和响应式
    • 关键资源添加预加载
  4. 构建优化

    • 配置代码拆分配置
    • 移除未使用代码 (Tree Shaking)
    • 压缩生产环境资源
  5. 高级特性

    • 长列表使用虚拟化技术
    • 动画使用 CSS transform/will-change
    • 耗时操作使用 useTransition

总结:性能优化决策树

1
2
3
4
5
6
7
8
9
10
graph TD
A[性能问题] --> B{组件重渲染过多?}
B -->|是| C[使用 React.memo/useMemo/useCallback]
B -->|否| D{状态更新效率低?}
D -->|是| E[优化状态结构/批量更新]
D -->|否| F{资源加载慢?}
F -->|是| G[代码分割/预加载/懒加载]
F -->|否| H{大数据量操作?}
H -->|是| I[虚拟化/时间分片/Web Worker]
H -->|否| J[使用 Performance 工具深入分析]

通过组合使用这些策略,即使不使用 Performance 工具,也能显著提升 React 应用性能。最佳实践是预防优于修复 - 在开发过程中持续应用这些模式,而非在性能问题出现后才开始优化。

006-React Fiber详解

React Fiber是React 16中引入的新的协调算法,它重新实现了React的核心算法,旨在解决一些长期存在的问题,并提供了更好的性能,尤其是对动画、布局和手势等需要高响应性的领域。同时,它也为未来的特性(如异步渲染)奠定了基础。

为什么需要Fiber?

在React 15及之前,React使用递归方式处理组件树。当React开始渲染时,它会递归地遍历整个组件树,并且一旦开始就不能中断。如果组件树很大,主线程可能会被长时间占用,导致无法及时响应用户交互(如动画、输入等),造成卡顿。

Fiber的目标是:

  1. 可中断任务:将渲染任务拆分成多个小任务,可以按优先级执行,高优先级任务(如用户输入)可以打断低优先级任务(如渲染)。
  2. 增量渲染:允许React将渲染工作分批进行,而不是一次性完成整个组件树。
  3. 更好的错误处理:引入错误边界(Error Boundaries)来捕获和处理组件树中的错误。

Fiber的核心概念

Fiber节点

在Fiber架构中,React将每个组件表示为一个Fiber节点。整个组件树被转换成一个由Fiber节点构成的链表(Fiber树)。每个Fiber节点包含了组件的类型、状态、props、子节点、兄弟节点、父节点等信息。

双缓存技术

React在更新过程中会同时存在两棵Fiber树:

  • current树:当前屏幕上显示内容对应的Fiber树。
  • workInProgress树:正在构建的新的Fiber树(在内存中构建)。

当workInProgress树构建完成并渲染到屏幕上后,current指针就会指向workInProgress树,而workInProgress树则变为current树。这种技术称为双缓存,它能够保证在更新过程中屏幕内容不会出现断裂(即不会显示不完整的渲染结果)。

任务拆分与调度

Fiber将渲染过程拆分为两个阶段:

  1. Reconciliation(协调/渲染阶段)
  • 这个阶段可以被打断。React会生成新的Fiber树(workInProgress树),并通过diff算法计算出需要进行的更新(即副作用,如增删改节点)。
  • 这个阶段会执行组件的render方法,并标记需要更新的DOM节点。
  1. Commit(提交阶段)
  • 这个阶段是同步的,不能被打断。React将上阶段计算出的副作用一次性提交到DOM上,更新UI。
  • 这个阶段会执行生命周期方法(如componentDidMountcomponentDidUpdate)以及hooks中的useLayoutEffect

优先级

React为不同的更新任务分配了优先级:

  • Immediate:需要立即执行,例如用户输入。
  • UserBlocking:用户交互的结果,例如点击事件。
  • Normal:默认优先级,例如普通的更新。
  • Low:低优先级任务,例如数据拉取。
  • Idle:空闲时执行的任务。

React使用浏览器的requestIdleCallbackrequestAnimationFrame来调度任务,确保高优先级任务优先执行。

Fiber的工作流程

协调阶段(可中断)

  1. 开始:从根节点开始,React会遍历每个Fiber节点。
  2. 构建workInProgress树
  • 对于每个Fiber节点,React会复制当前节点(current)来创建workInProgress节点(如果不存在则新建)。
  • 然后调用组件的render方法(函数组件则调用函数本身)获取子元素,并与旧的子元素进行对比(diff算法),生成子节点的Fiber节点。
  • 为每个子元素创建或更新对应的Fiber节点,并建立父子、兄弟关系。
  1. 收集副作用:在遍历过程中,标记需要更新的节点(如需要插入、更新、删除的节点),这些副作用会被记录在Fiber节点上(effectTag),并形成一个副作用链表(effect list)。

提交阶段(不可中断)

  1. 处理副作用:遍历副作用链表,执行DOM更新(增删改节点)。
  2. 调用生命周期方法:在DOM更新后,调用组件的componentDidMountcomponentDidUpdate等生命周期方法,以及hooks中的useLayoutEffect
  3. 切换current树:将workInProgress树设置为current树。

异步渲染

Fiber架构使得React能够实现异步渲染(Concurrent Mode)。在异步渲染模式下,React可以在多个帧之间分段执行渲染任务,并在浏览器空闲时执行低优先级任务,从而避免主线程长时间被占用。

错误边界

Fiber架构引入了错误边界(Error Boundaries)的概念。错误边界是一个组件,它可以捕获子组件树中任何位置的JavaScript错误,并记录这些错误,同时显示降级UI。这通过组件的static getDerivedStateFromError()componentDidCatch()生命周期方法实现。

总结

React Fiber是React核心算法的重构,它解决了之前版本中由于递归渲染导致的无法中断的问题。Fiber将渲染任务拆分成多个小任务,并赋予优先级,使得高优先级任务可以打断低优先级任务,从而提升用户体验。同时,它也为异步渲染和错误边界等特性提供了基础。

Fiber架构主要包含两个阶段:可中断的协调阶段和不可中断的提交阶段。在协调阶段,React构建workInProgress树并收集副作用;在提交阶段,React将副作用应用到DOM上并执行相应的生命周期方法。

React Fiber 架构深度解析:原理、机制与实战优化

React Fiber 是 React 16 引入的核心重写架构,彻底改变了 React 的渲染机制。它解决了大型应用中的性能瓶颈问题,并为异步渲染、优先级调度等高级特性奠定了基础。

一、Fiber 诞生的背景与挑战

传统 Stack Reconciler 的问题

1
2
3
4
5
6
7
graph TD
A[setState] --> B[开始渲染]
B --> C[递归处理组件树]
C --> D[深度优先遍历]
D --> E[不可中断]
E --> F[主线程阻塞]
F --> G[动画卡顿/输入延迟]

在 React 15 及之前版本中:

  • 递归渲染:深度优先遍历整个组件树
  • 同步阻塞:无法中断渲染过程
  • 无优先级:所有更新任务平等处理
  • 性能瓶颈:大型应用渲染时间超过 16ms(60fps 标准)

浏览器渲染机制限制

[  16ms 帧时间  ]
├── JavaScript 执行
├── 样式计算
├── 布局
├── 绘制
└── 空闲时间 ← Fiber 利用这里

二、Fiber 架构核心原理

1. Fiber 节点数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// 实例标识
this.tag = tag; // 组件类型(函数/类/Host等)
this.key = key; // 唯一标识
this.elementType = null; // 元素类型
this.type = null; // 构造函数
this.stateNode = null; // 组件实例

// 树结构关系
this.return = null; // 父节点
this.child = null; // 首子节点
this.sibling = null; // 兄弟节点

// 状态与属性
this.pendingProps = pendingProps; // 新 props
this.memoizedProps = null; // 当前 props
this.memoizedState = null; // 当前 state
this.updateQueue = null; // 更新队列

// 副作用标记
this.flags = NoFlags; // 操作类型(增/删/改)
this.subtreeFlags = NoFlags; // 子树副作用
this.deletions = null; // 待删除节点

// 调度优先级
this.lanes = NoLanes; // 任务优先级
this.childLanes = NoLanes; // 子树优先级

// 双缓存指针
this.alternate = null; // 指向另一棵树节点
}

2. Fiber 树与双缓存机制

1
2
3
4
5
6
7
8
graph LR
A[Current Tree] -->|显示在屏幕| B[用户界面]
C[WorkInProgress Tree] -->|构建中| D[内存]

A -->|alternate| C
C -->|alternate| A

E[提交阶段] -->|切换指针| F[WorkInProgress 成为 Current]

3. 渲染流程:可中断的异步渲染

1
2
3
4
5
6
7
8
9
10
11
graph TB
A[触发更新] --> B{调度开始}
B --> C[创建WorkInProgress树]
C --> D[遍历Fiber节点]
D --> E{时间片用完?}
E -->|是| F[暂停并返回控制权]
E -->|否| G[处理当前节点]
G --> H[完成所有节点?]
H -->|否| D
H -->|是| I[提交更新]
I --> J[切换Current指针]

三、Fiber 核心机制详解

1. 优先级调度(Lane 模型)

React 18 引入的 Lane 优先级系统:

1
2
3
4
5
6
export const NoLanes: Lanes = 0b0000000000000000000000000000000;
export const SyncLane: Lanes = 0b0000000000000000000000000000001; // 最高优先级
export const InputContinuousLane: Lanes = 0b0000000000000000000000000000100;
export const DefaultLane: Lanes = 0b0000000000000000000000000010000;
export const TransitionLane: Lanes = 0b0000000000000000000010000000000; // 18新增
export const IdleLane: Lanes = 0b0100000000000000000000000000000; // 最低

调度策略

  • 用户输入 > 动画 > 普通更新 > 后台任务
  • 高优先级任务可中断低优先级任务

2. 渲染阶段分解

阶段1: Render Phase(可中断)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function performUnitOfWork(fiber) {
// 1. 开始工作(beginWork)
const next = beginWork(fiber);

if (next) {
// 存在子节点,继续处理
return next;
}

// 2. 完成工作(completeWork)
let sibling = completeWork(fiber);

if (sibling) {
// 存在兄弟节点,处理兄弟
return sibling;
}

// 3. 回溯父节点
return completeWork(fiber.return);
}

阶段2: Commit Phase(不可中断)

1
2
3
4
5
6
7
8
9
10
11
12
13
function commitRoot(root) {
// 1. 副作用提交前
commitBeforeMutationEffects();

// 2. DOM 变更
commitMutationEffects();

// 3. 切换当前树
root.current = finishedWork;

// 4. 副作用提交后
commitLayoutEffects();
}

3. 副作用(Effects)处理

React 将 DOM 操作抽象为副作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 副作用链表结构
function commitMutationEffects() {
let effect = firstEffect;

while (effect !== null) {
const next = effect.nextEffect;

switch (effect.flags) {
case Placement:
commitPlacement(effect);
break;
case Update:
commitUpdate(effect);
break;
case Deletion:
commitDeletion(effect);
break;
// ...其他副作用
}
effect = next;
}
}

四、Fiber 带来的革命性特性

1. 并发模式(Concurrent Mode)

1
2
3
4
5
6
7
// 启用并发模式
import * as ReactDOM from 'react-dom/client';

const root = ReactDOM.createRoot(
document.getElementById('root')
);
root.render(<App />);

并发特性

  • useTransition():非阻塞UI更新
  • useDeferredValue():延迟更新
  • <Suspense>:数据加载处理

2. 可中断渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
// 模拟高开销渲染
function HeavyComponent() {
const items = [];

// 传统React会阻塞主线程
for (let i = 0; i < 100000; i++) {
items.push(<div key={i}>Item {i}</div>);
}

return <div>{items}</div>;
}

// Fiber下可被中断,保持UI响应

3. 错误边界(Error Boundaries)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ErrorBoundary extends React.Component {
state = { hasError: false };

static getDerivedStateFromError(error) {
return { hasError: true };
}

componentDidCatch(error, info) {
logError(error, info);
}

render() {
return this.state.hasError
? <FallbackUI />
: this.props.children;
}
}

五、Fiber 性能优化实战

1. 渲染性能分析

使用 React DevTools 分析组件渲染:

1
2
3
4
5
6
7
// 检测不必要的渲染
function MyComponent(props) {
// 使用React.memo优化
return <ExpensiveChild />;
}

export default React.memo(MyComponent);

2. 优先级控制示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { startTransition, useDeferredValue } from 'react';

function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);

const handleChange = (e) => {
const value = e.target.value;
setQuery(value);

// 标记为非紧急更新
startTransition(() => {
setResults(fetchResults(value));
});
};

return (
<>
<input value={query} onChange={handleChange} />
<Results query={deferredQuery} />
</>
);
}

3. 虚拟化长列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { FixedSizeList } from 'react-window';

const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);

const App = () => (
<FixedSizeList
height={600}
width={300}
itemSize={50}
itemCount={1000}
>
{Row}
</FixedSizeList>
);

六、Fiber 与传统架构对比

特性 Stack Reconciler (React 15) Fiber (React 16+)
渲染机制 递归(不可中断) 链表遍历(可暂停/恢复)
任务调度 同步 优先级异步调度
渲染阶段 单阶段(commit) 双阶段(render/commit)
最大优势 实现简单 高性能、高响应性
支持特性 基础更新 并发渲染、Suspense
错误处理 整个应用崩溃 错误边界局部捕获
适用场景 简单应用 复杂企业级应用

七、Fiber 最佳实践指南

  1. 组件设计原则

    • 拆分大型组件
    • 使用 React.memo 优化
    • 避免内联函数/对象
  2. 状态管理优化

    1
    2
    3
    4
    // 使用useState惰性初始化
    const [data, setData] = useState(() => {
    return computeExpensiveValue();
    });
  3. 并发模式迁移

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 渐进式迁移策略
    import { createRoot } from 'react-dom/client';
    import { createBlockingRoot } from 'react-dom';

    // 关键路径使用传统模式
    const criticalRoot = createBlockingRoot(container);
    criticalRoot.render(<CriticalApp />);

    // 非关键路径使用并发模式
    const concurrentRoot = createRoot(nonCriticalContainer);
    concurrentRoot.render(<NonCriticalApp />);
  4. Suspense 数据获取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 创建资源包装器
    function fetchUser(id) {
    let status = 'pending';
    let result;
    let suspender = fetch(`/users/${id}`)
    .then(response => response.json())
    .then(data => {
    status = 'success';
    result = data;
    });

    return {
    read() {
    if (status === 'pending') throw suspender;
    if (status === 'error') throw result;
    return result;
    }
    };
    }

    // 在组件中使用
    function UserProfile({ id }) {
    const user = userResource.read();
    return <div>{user.name}</div>;
    }

八、Fiber 未来演进方向

  1. 离屏渲染(Offscreen Rendering)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { Offscreen } from 'react';

    function App() {
    return (
    <>
    <MainContent />
    <Offscreen mode="hidden">
    <ExpensiveSidebar />
    </Offscreen>
    </>
    );
    }
  2. 服务器组件(Server Components)

    1
    2
    3
    4
    5
    // 服务端组件(不发送到客户端)
    async function Note({ id }) {
    const note = await db.notes.get(id);
    return <NoteView note={note} />;
    }
  3. 响应式 Hooks

    1
    2
    3
    4
    5
    6
    import { use } from 'react';

    function Weather() {
    const data = use(fetchWeather());
    return <div>{data.temperature}°C</div>;
    }