React 的架构设计演变
#react
目录
- 1. 总结
- 2. 前端框架性能问题都可以归因以下两个
- 3. React 架构演变
- 4. 架构分层与核心包的关系
1. 总结
- 性能瓶颈来源,就==两个==
- cpu 瓶颈
- io 瓶颈
- 架构演变
- 15 及之前 → 协调器 + 渲染器
- 16 及之后 → 协调器 + 渲染器 + 调度器
- 可中断更新
2. 前端框架性能问题都可以归因以下两个
2.1. CPU 瓶颈
- JavaScript 是单线程执行的
- 在主线程上进行大量计算会阻塞渲染
- 长任务(>50ms)会导致页面响应迟钝
- 比如同时渲染 30000 个 DOM
- vdom 相关的处理
- 所以,要求 React 具有将长任务拆解的能力,即时间切片
time slice
- 所以,要求 React 具有将长任务拆解的能力,即时间切片
未使用时间切片的效果如下:
使用时间切片的效果如下:很平滑
2.2. I/O 瓶颈
- 主要的
I/O 瓶颈
是网络 - 又比如输入打字
- 解决方案
- 结合==人机交互==,用户对不同的操作的感知敏感度不一样,比如
- 键盘输入到显示,稍微延迟,用户也能感觉到
- 鼠标悬停,稍微延迟,用户也能感觉到
- 点击按钮,到数据显示,有点延迟用户也能接受
- 所以,对不同操作,要求 React 具有
- 有优先级调度的能力,这就要求
- 调用算法
- 可中断 VDOM,毕竟不中断,CPU 一直占着
- 有优先级调度的能力,这就要求
- 结合==人机交互==,用户对不同的操作的感知敏感度不一样,比如
3. React 架构演变
3.1. React 15 :Reconciler 架构
3.1.1. Reconciler 协调器
同步的,VDOM 的实现
// React15 中的递归处理示意
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren) {
// 同步递归处理,无法中断
for (let i = 0; i < newChildren.length; i++) {
reconcileChildrenArray(/*...*/)
}
}
3.1.2. Renderer 渲染器
负责将 UI变化渲染到宿主环境
3.2. React 16 :支持时间切片的 Fiber Reconciler
- 新引入:
Scheduler 调度器
- 调度任务的优先级,高优的优先进入 Reconciler
Reconciler 协调器
:- VDOM 的实现,计算出 UI 的变化
- Renderer 渲染器:
- 负责将 UI 变化渲染到宿主环境
3.2.1. 演示更新效果
3.3. Legacy(遗产) 模式(React 16/17 默认模式)
这是 React 最古老的渲染模式,通过 ReactDOM.render()
创建应用。
// Legacy 模式
import ReactDOM from 'react-dom';
ReactDOM.render(
<App />,
document.getElementById('root')
);
3.3.1. 特点:
- 同步渲染
- 不支持新的并发特性
- 更新是同步的且不可中断
3.4. Blocking 模式(React 16.x 实验性)
这是向并发模式过渡的中间模式。
createBlockingRoot
// Blocking 模式
import ReactDOM from 'react-dom';
ReactDOM.createBlockingRoot(
document.getElementById('root')
).render(<App />);
3.4.1. 特点:
- 部分并发模式特性
- 比 Legacy 模式更接近并发
- 作为过渡阶段的模式
3.5. Concurrent 模式(React 18+)
最新的渲染模式,支持所有新特性
createRoot
// Concurrent 模式
import ReactDOM from 'react-dom/client';
// 创建根节点
const root = ReactDOM.createRoot(
document.getElementById('root')
);
// 渲染应用
root.render(<App />);
3.5.1. 特点:
- 完整的并发特性支持
- 可中断的渲染
- 自动批处理
- 优先级调度
3.5.2. 示例:使用 startTransition 将状态更新标记为非紧急,降低优先级
// Concurrent 模式特性示例
function ConcurrentComponent() {
const [isPending, startTransition] = useTransition();
const [list, setList] = useState([]);
const [query, setQuery] = useState('');
// 搜索处理
const handleSearch = (e) => {
// 立即更新输入值(高优先级)
setQuery(e.target.value);
// 将搜索结果更新标记为转换(低优先级)
startTransition(() => {
const searchResults = performExpensiveSearch(e.target.value);
setList(searchResults);
});
};
return (
<div>
<input
value={query}
onChange={handleSearch}
placeholder="Search..."
/>
{isPending ? (
<div>Loading...</div>
) : (
<ul>
{list.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)}
</div>
);
}