Fiber 数据结构
我们先回顾一下 Fiber 节点的数据结构(之前文章省略了一部分属性,所以和之前文章略有不同):
-
function FiberNode (tag, key) {
-
// 节点 key,主要用于了优化列表 diff
-
this.key = key
-
// 节点类型;FunctionComponent: 0, ClassComponent: 1, HostRoot: 3 ...
-
this.tag = tag
-
-
// 子节点
-
this.child = null
-
// 父节点
-
this.return = null
-
// 兄弟节点
-
this.sibling = null
-
-
// 更新队列,用于暂存 setState 的值
-
this.updateQueue = null
-
// 新传入的 props
-
this.pendingProps = pendingProps;
-
// 之前的 props
-
this.memoizedProps = null;
-
// 之前的 state
-
this.memoizedState = null;
-
-
// 节点更新过期时间,用于时间分片
-
// react 17 改为:lanes、childLanes
-
this.expirationTime = NoLanes
-
this.childExpirationTime = NoLanes
-
-
// 对应到页面的真实 DOM 节点
-
this.stateNode = null
-
// Fiber 节点的副本,可以理解为备胎,主要用于提升更新的性能
-
this.alternate = null
-
-
// 副作用相关,用于标记节点是否需要更新
-
// 以及更新的类型:替换成新节点、更新属性、更新文本、删除……
-
this.effectTag = NoEffect
-
// 指向下一个需要更新的节点
-
this.nextEffect = null
-
this.firstEffect = null
-
this.lastEffect = null
-
}
缓存机制
可以注意到 Fiber 节点有个 alternate 属性,该属性在节点初始化的时候默认为空(this.alternate = null)。这个节点的作用就是用来缓存之前的 Fiber 节点,更新的时候会判断 fiber.alternate 是否为空来确定当前是首次渲染还是更新。下面我们上代码:
-
import React from 'react';
-
import ReactDOM from 'react-dom';
-
-
class App extends React.Component {
-
state = { val: 0 }
-
render() {
-
return <div>val: { this.state.val }</div>
-
}
-
}
-
-
ReactDOM.unstable_createRoot(
-
document.getElementById('root')
-
).render(<App />)
在调用 createRoot 的时候,会先生成一个FiberRootNode,在 FiberRootNode 下会有个 current 属性,current 指向 RootFiber 可以理解为一个空 Fiber。后续调用的 render 方法,就是将传入的组件挂载到 FiberRootNode.current(即 RootFiber) 的空 Fiber 节点上。
-
// 实验版本对外暴露的 createRoot 需要加上 `unstable_` 前缀
-
exports.unstable_createRoot = createRoot
-
-
function createRoot(container) {
-
return new ReactDOMRoot(container)
-
}
-
function ReactDOMRoot(container) {
-
var root = new FiberRootNode()
-
// createRootFiber => createFiber => return new FiberNode(tag);
-
root.current = createRootFiber() // 挂载一个空的 fiber 节点
-
this._internalRoot = root
-
}
-
ReactDOMRoot.prototype.render = function render(children) {
-
var root = this._internalRoot
-
var update = createUpdate()
-
update.payload = { element: children }
-
const rootFiber = root.current
-
// update对象放到 rootFiber 的 updateQueue 中
-
enqueueUpdate(rootFiber, update)
-
// 开始更新流程
-
scheduleUpdateOnFiber(rootFiber)
-
}
render 最后调用 scheduleUpdateOnFiber 进入更新任务,该方法之前有说明,最后会通过 scheduleCallback 走 MessageChannel 消息进入下个任务队列,最后调用 performConcurrentWorkOnRoot 方法。
-
// scheduleUpdateOnFiber
-
// => ensureRootIsScheduled
-
// => scheduleCallback(performConcurrentWorkOnRoot)
-
function performConcurrentWorkOnRoot(root) {
-
renderRootConcurrent(root)
-
}
-
function renderRootConcurrent(root) {
-
// workInProgressRoot 为空,则创建 workInProgress
-
if (workInProgressRoot !== root) {
-
createWorkInProgress()
-
}
-
}
-
function createWorkInProgress() {
-
workInProgressRoot = root
-
var current = root.current
-
var workInProgress = current.alternate;
-
if (workInProgress === null) {
-
// 第一次构建,需要创建副本
-
workInProgress = createFiber(current.tag)
-
workInProgress.alternate = current
-
current.alternate = workInProgress
-
} else {
-
// 更新过程可以复用
-
workInProgress.nextEffect = null
-
workInProgress.firstEffect = null
-
workInProgress.lastEffect = null
-
}
-
}
开始更新时,如果 workInProgress 为空会指向一个新的空 Fiber 节点,表示正在进行工作的 Fiber 节点。

(编辑:应用网_丽江站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|