本次React源码参考版本为17.0.3
。
查阅文档了解到, React@16.x
是个分水岭。
在16之前,React架构大致可以分为两层:
但是React团队意识到这样的架构有致命问题: 因为在React15中,组件的更新是基于递归查找实现的,这样一旦开始递归,是没有办法中断的,如果组件层级很深,就会出现性能问题,导致页面卡顿。
为了解决这样的问题,React团队在React@16
进行了重构,引入了新的架构模型:
新的架构在原来的基础上引入了Scheduler(调度器)
,这个东西是React团队参考浏览器的API:requestIdleCallback
实现的。它的主要作用就是调度更新任务:
这个新的架构在进入Renderer之前的流程是可以被中断的,主要有下列两种情况:
Fiber简单的理解就是React15
版本的虚拟DOM。
如果将新的React架构比作一个公司,Fiber在新的架构里承担的就是这个公司的员工,员工也有等级,老板,部长,基层,每个人有自己的职责,知道自己在哪个节点该做什么工作,并将未完成的工作记住等第二天上班继续完成,从而保证公司的顺利运行。而每个Fiber对应一个React element
:
假如有这样一段代码:
function App() {
return (
<div>
<span>牛牛</span>
<span>不怕困难</span>
</div>
)
}
上面的代码的抽象Fiber树:
来看一个Fiber会有哪些属性:
function FiberNode(tag, pendingProps, key, mode) {
// Instance
this.tag = tag; // 组件类型
this.key = key; // 组件props上的key
this.elementType = null; // ReactElement.type 组件的dom类型, 比如`div, p`
this.type = null; // 异步组件resolved之后返回的内容
this.stateNode = null; // 在浏览器环境对应dom节点
this.return = null; // 指向父节点
this.child = null; // 孩子节点
this.sibling = null; // 兄弟节点, 兄弟节点的return指向同一个父节点
this.index = 0;
this.ref = null; // ref
this.pendingProps = pendingProps; // 新的props
this.memoizedProps = null; // 上一次渲染完成的props
this.updateQueue = null; // 组件产生的update信息会放在这个队列
this.memoizedState = null; // // 上一次渲染完成的state
this.dependencies = null;
this.mode = mode; // Effects
this.flags = NoFlags; // 相当于之前的effectTag, 记录side effect类型
this.nextEffect = null; // 单链表结构, 便于快速查找下一个side effect
this.firstEffect = null; // fiber中第一个side effect
this.lastEffect = null; // fiber中最后一个side effect
this.lanes = NoLanes; // 优先级相关
this.childLanes = NoLanes; // 优先级相关
this.alternate = null; // 对应的是current fiber
}
在弄明白Fiber工作原理之前,我们要先明确一个认知:新的React架构使用了两个Fiber树。
current
;workInProgress
;这样做是为了方便比对变化组件,并降低创建的成本,尽可能复用现有代码逻辑,从而提高渲染效率。
React代码在第一次执行时,因为页面还没有渲染出来,此时是没有current
树的,只有一个正在构建DOM的workInProgress
树。
假如我们有这样一段代码:
function App() {
return (
<div>
<span>牛牛</span>
<span>不怕困难</span>
</div>
)
}
ReactDOM.render(<App/>, document.querySelector('#root'));
基于上面的代码在mount
会生成这样的Fiber树:
可以看到这个图只是在前面的图上增加了fiberRoot
和rootFiber
两个Fiber节点。
React.render()
函数,这样就会有多个rootFiber)图中此时fiberRoot对应的rootFiber下面还是空的,因为此时是第一次渲染,页面上没有任何东西,当workInProgress
树构建完成,在mutation
之后,layout
之前,fiberRootd的current
指针会指向workInProgress
树,把它作为新的current
树,此时结构会变成这样:
如果我们在上面的代码中触发更新,将牛牛
文本改成了勇敢牛牛
,React代码就会开始进行任务调度,因为只有这一个任务,会马上执行,会从current
树的rootFiber进行拷贝生成workInProgress
树的根节点,在经过向下遍历比对,发现相同的就直接从current
树上拷贝复用,直到比对到叶子节点的牛牛
文本变了,这时才会生成新的Fiber(这里只是为了方便解释,其实我这里使用的代码牛牛
不会生成新的Fiber,因为是纯文本,只会替换父级节点的props)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。