react源码解析-debugger阶段-函数组件App的beginWork阶段

各种类型fiber的beginWork

函数组件的beginWork

接第一章,我们走完了createRoot到rootFiber执行完beginWork阶段,workInprogress.child也就是App函数的fiber被创建完毕之后的阶段。

  • rootFiber执行完递阶段之后,返回了App fiber,将App fiber赋值给workINprogress.
  • 当performUnitOfWork执行完毕之后,当前浏览器无多余时间,等待下次调度执行performConcurrentWorkOnRoot
  • 然后执行performUnitOfWork,去执行App fiber的beginwork。

这章继续了解各种类型fiber的beginWork。

对于App fiber,当他执行beginwork的时候,是没有alterNate的,所以处于mount阶段。而且App是一个函数组件,而react事先不知道他是普通函数还是函数组件,所以App fiber的tag是2也就是

export const ClassComponent = 1;  // 类组件
export const IndeterminateComponent = 2; // 初始化的时候不知道是函数组件还是类组件 
IndeterminateComponent的beginWork

当App fiber进入beginWork的时候,

在这里插入图片描述

直接执行mountIndeterminateComponent函数。

这里需要注意一点,对于函数组建的fiber,长如下这样

函数组建的vdom
{
$$typeof: Symbol(react.element)
type: ƒ App() //函数
key: null,
ref: null,
props: {} 
}
类组件的vdom
{
$$typeof: Symbol(react.element)
key: null
props: {}
ref: null
type: ƒ DD()		//类
}
原生的vdom
{
$$typeof: Symbol(react.element)
type: "div", // type是标签
key: null,
ref: null,
props: {
    "children": "123123"
},
}
字符串数字不是vdom

// 函数组建的fiber
const fiber = {
	alternate: null,
	elementType: ƒ App(),
	type: ƒ App(),
	updateQueue: null,
	tag: 2,
	return: parentFiber,
	sibling: siblingFiber,
	.....
}

所以fiber.type就是指App函数本身。

mountIndeterminateComponent
// 对于第一次执行的函数组件,react不知道他是函数组件还是普通函数,统统转为indeterminateComponent,即不清楚的component
function mountIndeterminateComponent(
  _current,
  workInProgress,
  Component,
  renderLanes,
){...
	 value = renderWithHooks(
      null,
      workInProgress,
      Component,
      props,
      context,
      renderLanes,
    );
    
    。。。。
    
    if( !disableModulePatternComponents &&
    typeof value === 'object' &&
    value !== null &&
    typeof value.render === 'function' &&
    value.$$typeof === undefined){
    ...
     workInProgress.tag = ClassComponent; //判断为类组件
     initializeUpdateQueue(workInProgress); //初始化updateQueue
   	 adoptClassInstance(workInProgress, value);
     mountClassInstance(workInProgress, Component, props, renderLanes); //实例化
     return finishClassComponent( //返回render内容。
      null,
      workInProgress,
      Component,
      true,
      hasContext,
      renderLanes,
    );
    } else {
     // Proceed under the assumption that this is a function component
    workInProgress.tag = FunctionComponent; //标识为函数组件
    reconcileChildren(null, workInProgress, value, renderLanes); //调度儿子生成fiber
    return workInProgress.child;
    }
    

..}

这个renderWithHooks也很重要,他的作用就是来执行函数,获取函数组件返回的vdom。

renderWithHooks
 function renderWithHooks<Props, SecondArg>(
  current: Fiber | null,  //alternate
  workInProgress: Fiber,   // App
  Component: (p: Props, arg: SecondArg) => any,	// APp函数
  props: Props,		// props
  secondArg: SecondArg,
  nextRenderLanes: Lanes,
){

 ReactCurrentDispatcher.current = // 赋值React hooks
      current === null || current.memoizedState === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;
        
  // 执行函数组件
  let children = Component(props, secondArg);

  // 执行完函数组件后,赋值hooks为ContextOnlyDispatcher,那么函数组件执行后调用比如useState的时候就会报错。
  ReactCurrentDispatcher.current = ContextOnlyDispatcher;

  return children; // 函数组件返回的vdom
}
  • 简化后的renderWithHooks就做了上述这些事情,函数组件执行的时候,可能会有一些hooks,比如useState,当函数组件执行之前,会通过当前是mount还是update,赋值HooksDispatcherOnMount或者是HooksDispatcherOnUpdate。
  • 然后执行函数,获取return的vdom。
  • 然后继续赋值hooks为ContextOnlyDispatcher(用来抛错的),这样如果有hooks在函数组件执行完后再执行Hooks的话,就会报错。因为此时hooks被赋值了ContextOnlyDispatcher

执行完renderWithHooks后,可以看到,他会根据函数执行返回的value判断当前FIber是类组件还是函数组件,因为rooFIber的儿子也就是App有可能是一个类。这里我们看函数组件的逻辑。

这里有一个点,所有的函数组件在Mount的时候,都会标识为IndeterminateComponent,等到执行完renderWithHooks的时候,才能确定是函数组件,才会打上函数组建的标识。在Update的时候就不会走mountIndeterminateComponent的逻辑了。

执行完renderWithHooks后,标识当前Appfiber为函数组件,然后执行

 reconcileChildren(null, workInProgress, value, renderLanes);
 
 function reconcileChildren(
  current: Fiber | null,
  workInProgress: Fiber,
  nextChildren: any, //即将处理的vdom
  renderLanes: Lanes,
) {}

第三个参数是value,也就是将要处理的子节点,会根据他生成fiber

此时Appfiber是没有alternate的,所以走mountChildFibers逻辑。

在这里插入图片描述

他的逻辑跟rootFiber调用reconcileChildren差不多,只不过这一次,是mountCHildFibers,

export const reconcileChildFibers = ChildReconciler(true);
export const mountChildFibers = ChildReconciler(false);

ChildReconciler返回reconcileChildFibers。所以mountChildFibers和reconcileChildFibers都是调用reconcileChildFibers。

reconcileChildFibers

在这里插入图片描述

reconcileSingleElement会根据vdom创建fiber,并且将刚创建的fiber跟workInprogress连接起来。这里如果有其它类型的vdom

在这里插入图片描述

还可能出现多个儿子的。多个儿子会全部创建fiber,然后通过return 和sibling指针关联,然后返回大儿子,后续会详细看。
在这里插入图片描述

还有是字符串或者数字的。会单独处理,不会生成fiber。做优化使用

在这里插入图片描述

而plceSingleChild会判断是否需要加上flags

在这里插入图片描述

因为当前是mount,所以传入的是false,并且APp fiber没有alternate,所以,不会加上flags。这也验证了,第一次mount的时候,只有rooFiber的儿子App fiber会打上Plcament标记。

mountIndeterminationComponent执行完reconcileChildren后,返回了workInprogress.child。也就是
在这里插入图片描述

我们的DD类组件的fiber,简称DDfiber。

  • 然后继续走之前rootFiber的逻辑,因为App fiber的beginWork返回了DD fiber,不会进入归阶段,将DD fiber赋值给workInprogress之后
  • 继续while循环,判断是否有多余的时间,没有的话等schedule调度我们注册的performConcurrenWorkOnRoot,
  • performConcurrenWorkOnRoot最终又会调用performUnitOfWork,执行workInprogress的beginWork,只不过这次workInprogress换成了DDfiber。
  • 自此,函数组件App fiber的beginWork阶段就到此结束,轮到DD fiber的beginWork了。
  • 总结:函数组件在被创建fiber的时候,tag会被标识为indeterminationComponent,即不确定的组件,因为react第一次无法确认她是否是真的函数组件,
  • 然后只能执行mountIndeterminationComponent方法,去执行renderWithHooks,这个方法会赋值hooks,执行函数,将函数的返回值返回,然后根据函数的返回值判断函数的类型,这时候才会将Fiber.tag标识为FunctionComponent,
  • 然后创建子fiber连接起来,将子fiber返回,完成Appfiber的beginWork阶段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值