从零实现react(3) hooks

diff dom

老知识点了,配置babel,对Jsx语法转换成React.createElement,
使用自己定义的createElement,

在这里插入图片描述
使用自己定义的createElement。
在这里插入图片描述
这个element的话Babel会转化成createElement,
在这里插入图片描述
虚拟dom就打印出来了。

实现初次渲染

思路
在这里插入图片描述
每个fiber有三个指针,return,child,sibling。顺序就是

代码实现

定义节点类型
在这里插入图片描述
实现render函数
在这里插入图片描述
render函数创建rootFiber节点,然后调用调度函数scheduleRoot函数。
react里面有好几个包,reconciler(协调) schedule(调度)

实现schedule

schedule有两个阶段,如图
在这里插入图片描述
这里的render函数并不是将虚拟dom转化成真实dom,而是根据虚拟dom生成fiber树,然后再收集effectList。
结合我们之前实现的fiber阶段和requestIdleCallback函数,来实现schedule函数完成调度功能。也就是我们熟悉的workLoop,performUnitOfWork这些函数的实现
在这里插入图片描述
定义两个变量指定根节点
在这里插入图片描述
利用requestIdleCallback执行workLoop函数,渲染fiber
在这里插入图片描述
每次进来都会判断当前是否还有空闲时间。没时间之后我就继续去请求requestIdleCallback函数,下次再执行该任务。
接着完成performUnitOfWork函数,该函数会返回下个该执行的任务
在这里插入图片描述
思路:对每个需要执行的任务调用BiginWork创建fiber,以根节点为例子,其会创建一个newFiber,然后将跟fiber的child指向该fiber,这样就可以顺理成章走第二步了。beginwork后的判断就是正常三步判断了,先判断儿子,没儿子了表明该任务完成了,该任务没弟弟,表明父fiber完成了(子fiber完成即认为完成)就直接返回父fiber继续走while循环,直到连currentFiber.return为null的时候,即根fiber完成了,就真正完成了。

beginwork函数实现

在这里插入图片描述
直接调用udateHostroot,我们先处理根节点的。
在这里插入图片描述
newchildren就是根节点了。
再调用reconcileChildren函数去处理根fiber和创建子fiber。
在这里插入图片描述
这个函数用来创建子fiber并创建联系,这样就可以通过currentFiber的child或者currentFiber.child.sibling这些来拿到哥哥弟弟等操作,只能建立一层联系,即父亲与儿子,孙子的联系是等到执行儿子的fiber时候才建立的。
在这里插入图片描述
当所有的完成后,看看completeUnitOfWork执行了什么
completeUnitOfWork主要是用来建立起一个单链表,使利用每个fIber的firstEffect,LastEffect以及NextEffect来建立一个单链表。
在这里插入图片描述
依照这个例子,最终的完成顺序应该是:
A1(text) => B1(text) => C1(text) => C1(div) => C2(text) => C2(div) => B1(div) => B2(text) => B2(div) =>A1(div)
文本节点,也是一个fiber。
全部代码:

//收集有副作用的fiber,组成effect list  每个fiber有两个属性,firstEffect指向第一个有副作用的子fiber 
//lastEffect 指向最后一个有副作用的子fiber 中间有副作用的用nextEffect做成一个单链表
// firstEffect(大儿子) firstEffec.nextEffect指向二儿子  firstEffec.nextEffect.nextEffect指向小儿子lastEffect(小儿子)
function completeUnitOfWork(currentFiber) {
  //将所有副作用挂载到根fiber上
  // 挂载到A1的顺序: C1 C2 B1 D1 D2 B2 A1
  let returnFiber = currentFiber.return;
  if (returnFiber) {
    //这一段是把自己儿子挂载到父亲上
    if (!returnFiber.firstEffect) {
      //A1 firstFIber=>C1
      returnFiber.firstEffect = currentFiber.firstEffect; // 到B1的时候 A1.firstEffect=>C1  
    }

    if (currentFiber.lastEffect) { //C2  D2
      if (returnFiber.lastEffect) { 
        // B1->D1
        returnFiber.lastEffect.nextEffect = currentFiber.firstEffect; //到B2的时候 A1.lastEffect(B1).netEffect=>D1 B1->D1
      }

      returnFiber.lastEffect = currentFiber.lastEffect;   // 到B1的时候 A1.lastFiber=>C2 到B2的时候 A1.lastEffect =>D2
    }

    //这一段是挂在完儿子后,挂载自己到父亲上。因为调用顺序就是完成顺序,当到B1的时候,C1 C2肯定执行过了。
    const effectTag = currentFiber.effectTag;
    if (effectTag) {
      //给底层人士用的C1 C2 B1 B2
      //如果父节点有lastFiber,那么此时先让lastFiber的下个节点指向currentFiber,再让lastFiber指向currentFiber(单链表结构)
      if (returnFiber.lastEffect) {
        // C2->B1
        returnFiber.lastEffect.nextEffect = currentFiber; //到B1的时候 C2=>A1 到B2的时候 D2.next=>B2
      } else {
        returnFiber.firstEffect = currentFiber;
      }
      // A1的last=>B1 完成C1->C2->B1,以此类推
      returnFiber.lastEffect = currentFiber; //到B1的时候 A1.lastEffect=>B1 到B2的时候 A1.last=>B2 (此时完成了C1,C2,B1,D1,D2,B2)
    }
  }
}

一步一步看
在这里插入图片描述
进来的第一个完成的肯定是A1text
A1text的父节点就是A1(div)
此时的A1text没有firstEffect或者lastEffect,就是说没有子节点,所以这一段不用注意,这一段是给有子节点的比如像B1,他必须将C1 C2先挂载上单链表后,才能挂载自己,
在这里插入图片描述
这段对A1 text来说就是让父节点 A1的firstEffect和LastEffect指向A1(text)
这样A1text就成功成为了单链表第一个节点。接着会继续通过firstEffect,lastEffect,nextEffect来建立每个联系,A1text会是第一个节点,A1(div)是最后一个上单链表的。
所以这段的作用主要就是让自己上单链表。
completeUnitOfWork的目的就是收集effect list,建立一个单链表。
在这里插入图片描述
在这里插入图片描述
逐步遍历链表上每个节点,然后执行commitWork函数。
在这里插入图片描述
在commitWork上,判断操作,是增加还是删除还是替换,目前只有增加,所以直接拿到retun父节点的stateNode直接执行appendChild即可。
比如对于A1text来说returnFIber就是A1(div),直接A1(div)后插入儿子即可。
在这里插入图片描述
第一步完成

小结:

react会通过workLoop结合requestIdleCallback来运行workLoop函数,workLoop会循环遍历执行performUnitOfWork函数,传入当前的节点,然乎返回下一次执行的节点。performUnitOfWork做了很多事情,首先BeginWOrk()方法为每个节点创建了fiber,并且建立了父亲与儿子的联系。其次,执行完beginwork的节点,当没有子节点后,(即子节点没有子节点或者弟弟节点的时候)就算完成,此时必须执行completeUnitOfWork函数来收集依赖,形成effect list。怎么收集呢,通过每个fiber的firstEffet lastEffect nextEffect来建立一个单链表结构,文本节点也是一个fiber哦,所以按道理第一个就是A1text,因为A1text没有子节点,所以其最先完成。所有fiber执行完completeUnitOfWork的时候,单链表已经完成,此时第一阶段完成,render阶段完成,此时到达提交阶段,必须执行commitRoot,这个方法会拿到根节点的第一个firstEffect,也就是A1text,然后循环遍历nextEffect,一个一个执行commitWork,判断其是增删改还是,做对应的操作。此时就算完成了第一个上dom树的步骤,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

coderlin_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值