Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React底层运行简记 #38

Open
HerryLo opened this issue Jan 11, 2023 · 0 comments
Open

React底层运行简记 #38

HerryLo opened this issue Jan 11, 2023 · 0 comments
Assignees
Labels
Blog 文章

Comments

@HerryLo
Copy link
Member

HerryLo commented Jan 11, 2023

React底层运行简记

1. JSX背后的功能模块是什么,这个功能模块做了哪些事情?

JSX通过babel语法转换之后,实际就是通过React.createElement函数来创建React元素。createElement接收三个参数type类型,config配置,children子元素。通过createElement就可创建出虚拟DOM对象。

2. React团队为什么要去掉旧的生命周期函数?

React16+引入Fiber架构,Fiber会将一个大的更新任务拆解为多个小任务,而且它是可中止,可恢复的。React16+生命周期被划分为rendercommit两个阶段。render阶段在执行过程中允许被打断,而commit阶段操作涉及真实DOM渲染,是不可打断的。

ComponentWillMount
ComponentWillUpdate
ComponentWillReceiveProps

这些生命周期,它们都处于render阶段,而在Fiber架构中,render阶段会被打断,重复被执行。在这些生命周期可能习惯做的事情可能有:setState、异步请求、操作真实DOM等。而在Fiber异步渲染控制下,这些生命周期可能会导致非常严重的bug(例如在这些废弃的生命周期中调用支付接口)。

3. 为什么是React Hooks?

相对于Class组件,函数组件更加轻量,更加符合UI=render(data)特点。同时在Fiber架构的加持下,Hooks的实现不是问题。配合函数组件的发展,Hooks应运而生,从而是函数组件真正把数据和渲染绑定到一起。当然Hooks也还是存在部分不足:部分周期不存在;不能很好的消化“复杂”,组件的拆分和组织是一个大的挑战,不易把握。

4. 为什么Hooks执行顺序如此重要?

Hooks本质是链表。例如 使用useText、useState创建state时,hook创建的state会以单链表形式保存,更新时,函数组件重新调用,hooks会依次遍历单链表,读取数据并更新,这一过程完全按照创建时的顺序来的。因此当更新时,位置一旦改变,执行顺序被替换,运行就会出现bug。

5. 调和(协调)和diff的关系或区别?

调和指的是虚拟DOM映射到真实DOM的过程。调和过程并不能喝diff画等号。调和是“使一致”的过程,而diff是“找不同”的过程,它只是“使一致”过程中的一个环节。(当然常说的调和相关问题多半就是diff过程的)

6. react的diff逻辑和思路?

1.因为时间付扎渡的原因,diff过程只针对同层的节点作比较;
2.对于同类型的组件,才有进一步对比的必要性;
3.对于列表组件,通过key属性来维持节点的稳定性,避免总是生产新节点;

7. setState的工作流是怎么样的?

非并发(**concurrent**)模式setState会出现异步和同步的现象。在生命周期和合成事件中是同步,而在setTimeout、setInterval、DOM原生函数等函数中是同步的。那么这是为什么尼?在合成事件或生命周期执行时,批量更新的任务锁就被开启了,我们所做的setState操作会被放入到批量更新队列中,直到函数执行完,批量更新的任务锁才会被关闭。批量更新的任务锁是一个同步操作,而一旦你在setTimeout函数使用setState,此时setTimeout函数回调会被放入下一个宏任务执行,而当setState执行时,批量更新的任务锁时关闭的,它就不会放入到批量更新队列中,而是直接执行。

并发(**concurrent**)模式setState不会出现异步和同步的现象。因为存在时间切片,只要当前时间片没有结束,依旧可以将多个 setState 合并成一个,即使是在setTimeout中被调用。而对于超过当前时间片的操作,会通过MessageChannel放入到下一个宏任务中继续执行。(MessageChannel接收消息的时机比 Promise 所在的 microTask 要晚,但是早于 setTimeout)

8. Stack Reconciler栈调和 有怎么样的局限性?

浏览器中Js线程和渲染线程是互斥的。这两个线程不能穿插执行,必须串行。而当Js线程长时间占用主线程,那么渲染线程的更新就不得不长时间的等待,这时就会导致页面卡顿。

Stack Reconciler栈调和是一个同步递归过程,虚拟DOM树diff算法遍历是深度优先遍历。由于它是同步的,不可在被打断。当处理结构复杂,体量庞大的虚拟DOM树时,Stack Reconciler时间会很长,以为这Js主线程长时间占用主线程,进而导致上述中说道的渲染卡顿/页面卡死。

9. 说一说Fiber架构?

特点:可中断、可恢复、存在优先级。

 Scheduler ————> Reconciler ————> Renderer
 更新优先级         找不同         渲染不同

Fiber架构模式下,每个更新任务会被赋予一个优先级。当然有任务A进入调度器,这个任务优先级更高,而Reconciler中已有任务B在执行,那么,Reconciler会将任务B终止,更高优先级的任务A被推入Reconciler。当A任务完成之后,新一轮调度会将之前中断的任务B重新推入Reconciler,继续它的渲染之旅。

render开始 ——————> (工作单元| 工作单元 | 工作单元) ——————> commit提交渲染

10. ReactDOM.render调用栈的初始化阶段、render阶段

初始化阶段:会创建root对象这个对象挂载_internalRoot属性,而_internalRoot也就是FiberRootFiberRoot的本质是一个FiberRootNode对象,其中包含current属性,current对象是一个FiberNode实例。current对象就是一个Fiber节点,并是Fiber树的头部节点;确定Fiber的优先级,结合优先级创建当前Fiber的update对象,并将其入队调度FiberRoot;接下来进入render阶段;(此时相当于只有一个Fiber头部节点)

render阶段:通过createWorkInProgress函数,创建rootFiber节点的副本workInProgress节点树(即current节点的副本节点),他们通过alternate互相引用;接着会触发beginWork函数,进而实现对新的Fiber节点的创建。循环遍历,组件元素Fiber会不断被创建(每个元素节点对应一个Fiber节点),直到创建到最后一个为止,此时Fiber树(单链表)基本完成;重点,此时已经遍历到了单链表的最底部节点,然后会由下自上的依次生成真实DOM节点,同时被它的父组件副作用链,这个副作用链也是一个单链表,直遍历到根节点,此时的根节点上的副作用链就包含的全部的DOM更新。那么剩下的只需要拿到root下的副作用链更新即可了。

@HerryLo HerryLo added the Blog 文章 label Jan 11, 2023
@HerryLo HerryLo self-assigned this Jan 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Blog 文章
Projects
None yet
Development

No branches or pull requests

1 participant