react源码解析-初次渲染(2)-Fiber树的构建

94 阅读2分钟

image.png 例子:

import React, { useState, useEffect } from "React";
import "./App.css";
function App() {
  const [num, setNum] = useState(0);
  const handleClick = () => {
    setNum(num + 1);
  };
  return (
    <div onClick={handleClick} className="parentDiv">
      <code key="child" title={num}>
        {num}
      </code>
    </div>
  );
}

fiber是根据什么构建的?

我们来阅读react的源码: 首先在这里打上断点,因为无论如何,只要新建fiber节点,都要通过这个函数

React App 360极速浏览器, Today at 10.57.35.png

image.png 发现fiber节点是根据value构建的

image.png

而value又是根据 value = renderWithHooks(null, workInProgress, Component, props, context, renderLanes)构建的,最后发现renderWithHook其实调用了 var children = Component(props, secondArg);,Component其实调用了我们使用的例子,最终结论就是fiber是根据jsx形成的虚拟dom构建的,这其实也是合理的。 我们在 root.current = finishedWork;打上断点发现fiber树和真实的dom结构很类似,区别就是有些节点有fiber而没有真实dom对应。

image.png

fiber结构

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // 作为静态数据结构的属性
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // 用于连接其他Fiber节点形成Fiber树
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  // 作为动态的工作单元的属性
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  // 调度优先级相关
  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  // 指向该fiber在另一次更新时对应的fiber
  this.alternate = null;
}

其中child,return和sibling用来搭建fiber树的

var created = createFiberFromFragment(element.props.children, returnFiber.mode, lanes, element.key);
created.return = returnFiber;
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);

pendingProps 表示本次需要的更新属性 memoizedProps上一次的属性

 var oldProps = current.memoizedProps;
 var newProps = workInProgress.pendingProps;
 var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
 workInProgress.updateQueue = updatePayload;

updateQueue 在commit阶段通过updateProperty更新真实的dom

workInProgress.updateQueue = updatePayload;

var updatePayload = finishedWork.updateQueue;
finishedWork.updateQueue = null;

if (updatePayload !== null) {
   commitUpdate(instance, updatePayload, type, oldProps, newProps);
}

初始化render commit各个阶段做了什么

首先创建fiberRoot 和 rootFiber

function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
  var root = new FiberRootNode(containerInfo, tag, hydrate);
  // stateNode is any.


  var uninitializedFiber = createHostRootFiber(tag);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;
  initializeUpdateQueue(uninitializedFiber);
  return root;
}

创建整个fiber树

生成真实的dom节点

var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
appendAllChildren(instance, workInProgress, false, false);
workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount.
// (eg DOM renderer supports auto-focus for certain elements).
// Make sure such renderers get scheduled for later work.

if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance)) {
  markUpdate(workInProgress);
}

渲染到页面 在commit阶段

if (isContainer) {
    insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
  } else {
    insertOrAppendPlacementNode(finishedWork, before, parent);
  }

在beginWork阶段中的 reconcileChildFibers 添加了placement标记