Skip to content

Commit

Permalink
add: comments
Browse files Browse the repository at this point in the history
  • Loading branch information
neroneroffy committed Jan 10, 2021
1 parent f86d5e9 commit 1a6d749
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 9,080 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# dependencies
/node_modules
/pics
/.pnp
.pnp.js
.idea
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,9 @@ concurrent模式下的lanes:/* */ 0b111111111111
- 属于concurrent模式的优先级:调用scheduleCallback将任务以上面获取到的新任务优先级去加入调度。

这里有两点需要说明:
1. 为什么新旧任务的优先级不相等,那么新任务的优先级一定高于旧任务?
1. 为什么新旧任务的优先级如果不相等,那么新任务的优先级一定高于旧任务?
这是因为每次调度去获取任务优先级的时候,都只获取root.pendingLanes中最紧急的那部分lanes对应的优先级,低优先级的update持有的lane对应的优先级是无法被获取到的。
通过这种办法,可以将来自同一事件中的多个更新收敛到一个任务中去执行,例如在一个事件中多次调用setState:
通过这种办法,可以将来自同一事件中的多个更新收敛到一个任务中去执行,言外之意就是同一个事件触发的多次更新的优先级是一样的,没必要发起多次任务调度。例如在一个事件中多次调用setState:
```javascript
class Demo extends React.Component {
state = {
Expand Down Expand Up @@ -317,7 +317,7 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);

// 获取renderLanes对应的优先级,它会作为新任务的优先级
// 获取renderLanes对应的任务优先级
const newCallbackPriority = returnNextLanesPriority();

if (nextLanes === NoLanes) {
Expand Down Expand Up @@ -363,12 +363,12 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
} else {
// concurrent模式的渲染会走这里

// 根据lanePriority获取Scheduler的调度优先级
// 根据任务优先级获取Scheduler的调度优先级
const schedulerPriorityLevel = lanePriorityToSchedulerPriority(
newCallbackPriority,
);

// 将React的更新任务放到Scheduler中去调度,调度优先级是schedulerPriorityLevel
// 计算出调度优先级之后,开始让Scheduler调度React的更新任务
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
Expand All @@ -381,8 +381,8 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
}
```

`ensureRootIsScheduled`实际上是在任务调度层面整合了高优先级任务的插队和任务饥饿问题的关键逻辑,这只是宏观层面的决策,决策背后的诱因是React处理更新时
对于不同优先级update的取舍以及root上pendingLanes的标记操作,这需要我们下沉到更新任务的执行过程中
`ensureRootIsScheduled`实际上是在任务调度层面整合了高优先级任务的插队和任务饥饿问题的关键逻辑,这只是宏观层面的决策,决策背后的原因是React处理更新时
对于不同优先级的update的取舍以及对root.pendingLanes的标记操作,这需要我们下沉到执行更新任务的过程中

# 处理更新
一旦有更新产生,update对象就会被放入updateQueue并挂载到fiber节点上。构建fiber树时,会带着renderLanes去处理updateQueue,在beginWork阶段,对于类组件
Expand Down
Binary file added docs/fiberTask4.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8,783 changes: 0 additions & 8,783 deletions docs/render阶段/beginWork阶段/setState异步原理/multiSetState.json

This file was deleted.

2 changes: 1 addition & 1 deletion docs/render阶段/completeWork/completeWork.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# 概述
每个WIP节点都会经历两个阶段:beginWork和completeWork。节点进入complete的前提是已经完成了beginWork。这个时候拿到的WIP节点都是
每个fiber节点在更新时都会经历两个阶段:beginWork和completeWork。节点进入complete的前提是已经完成了beginWork。这个时候拿到的WIP节点都是
经过diff算法调和过的,也就意味着对于某个WIP节点来说它fiber类型的形态已经基本确定了,但除此之外还有两点:

* 目前只有fiber形态变了,对于原生DOM组件(HostComponent)和文本节点(HostText)的fiber来说,对应的DOM节点(fiber.stateNode)并未变化。
Expand Down
152 changes: 0 additions & 152 deletions docs/setStateOverview.md

This file was deleted.

71 changes: 37 additions & 34 deletions docs/概述.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
React 进入调度有两种途径:挂载和更新,可以把他们理解成任务,调度的目的是依据优先级对任务进行合理的安排。

React 的Concurrent模式解决的问题是帮助应用保持响应,而交互和更新任务是互斥的。交互的最终结果是产生更新,进而生成任务进入调度流程。

如果此时已有任务在进行,但优先级低于新任务,那么它将会被取消,重新对新任务安排一个调度。

这里说的任务在React的内在行为上,就是从root开始,进入工作循环,构建workInProgress树。

任务调度

工作循环
概述
执行工作单元
render阶段
beginWork
处理更新
diff操作
completeWork
commit阶段
commitWork

更新的完整过程


金字塔结构

React 第一次渲染: ReactDOM.render
数据准备 调度 workLoop

React 更新: ReactDOM.render
事件系统 调度 workLoop



React原理,从更新说起。

作为一个构建用户界面的库,React的核心始终围绕着更新这一个重要的目标,将更新和极致的用户体验结合起来是React团队一直在努力的事情。为什么React可以将用户体验做到这么好?我想这是基于以下两点原因:

* Fiber架构和Scheduler出色的调度模式可以实现异步可中断的更新行为。
* 优先级机制贯穿更新的整个周期

下面我们基于这两点展开,在解读原理之前,对一些概念先有个基础认知。
# Fiber是什么
Fiber是什么?它是React的最小工作单元,在React的世界中,一切都可以是组件。在普通的HTML页面上,人为地将多个DOM元素整合在一起可以组成一个组件,HTML标签可以是组件(HostComponent),
普通的文本节点也可以是组件(HostText)。每一个组件就对应着一个fiber节点,许多个fiber节点互相嵌套、关联,就组成了fiber树,正如下面表示的Fiber树和DOM的关系一样:
```
Fiber树 DOM树
div#root div#root
| |
<App/> div
| / \
div p a
/ ↖
/ ↖
p ----> <Child/>
|
a
```
一个DOM节点一定对应着一个Fiber节点,但一个Fiber节点却不一定有对应的DOM节点。

每一个fiber节点都可能会产生更新,每当有更新的时候,React都会从root开始重新构建fiber树。这个行为,就是React的更新任务。对Fiber树来说,一旦有一个更新任务,它就会经历一个深度优先遍历的过程,
依照现有fiber树构建一个新的fiber树,现有fiber树称为current树,构建的新fiber树称为workInProgress树。整个fiber树会经历向下遍历以及向上回溯的两个过程,所以每个fiber节点,也会经历两个过程:
**beginWork****completeWork****构建过程中会有一个workInProgress的指针记录下当前构建到哪个fiber节点**,这是React更新任务可恢复的重要原因之一。fiber树的构建过程如下动图:

![fiberTask](http://neroht.com/fiberTask4.gif)


# 双缓冲机制
双缓冲机制

Loading

0 comments on commit 1a6d749

Please sign in to comment.