# React底层运行简记
# 1. JSX背后的功能模块是什么,这个功能模块做了哪些事情?
JSX
通过babel
语法转换之后,实际就是通过React.createElement
函数来创建React元素。createElement
接收三个参数type
类型,config
配置,children
子元素。通过createElement
就可创建出虚拟DOM
对象。
# 2. React团队为什么要去掉旧的生命周期函数?
React16+引入Fiber
架构,Fiber
会将一个大的更新任务拆解为多个小任务,而且它是可中止,可恢复的。React16+生命周期被划分为render
和commit
两个阶段。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属性来维持节点的稳定性,避免总是生产新节点;
2
3
# 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
更新优先级 找不同 渲染不同
2
在Fiber
架构模式下,每个更新任务会被赋予一个优先级。当然有任务A进入调度器,这个任务优先级更高,而Reconciler
中已有任务B在执行,那么,Reconciler
会将任务B终止,更高优先级的任务A被推入Reconciler
。当A任务完成之后,新一轮调度会将之前中断的任务B重新推入Reconciler
,继续它的渲染之旅。
render开始 ——————> (工作单元| 工作单元 | 工作单元) ——————> commit提交渲染
# 10. ReactDOM.render调用栈的初始化阶段、render阶段
初始化阶段:会创建root对象这个对象挂载_internalRoot
属性,而_internalRoot
也就是FiberRoot
。FiberRoot
的本质是一个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下的副作用链更新即可了。
男性,武汉工作,会点Web,擅长Javascript.
技术或工作问题交流,可联系微信:1169170165.