forseize

react面试题

react事件机制
在得到dom树之后,react会处理属性上是否有事件,react不会把事件绑定到真正的节点上,而是把所有的事件绑定在document(最外层节点)上,部分事件除外,如audio、video的onplay、onpause事件,这些事件是用原生标签进行代理,但仍由dispatchEvent进行绑定,并且将得到的事件类型和回调函数存储在映射表中,但对于同一事件类型,不管注册几次,最终只会保留一个有效实例。在事件触发中,开始dom流,捕获阶段,目标阶段,冒泡阶段,冒泡到document时,执行统一的dispatchEvent函数,根据原生对象获得事件触发的组件,根据事件类型生成相应的合成事件,并且封装原生事件和冒泡机制,获取所有父组件,根据映射表得到相应的事件回调,批量处理合成事件的回调。
对于原生事件,事件被触发就会创建一个事件对象,对于合成事件,有一个专门的事件池来管理事件的创建和销毁。当需要使用事件时,就会从事件池中复用对象,当事件结束时,就会销毁事件上的属性,以便下次再复用。这样的话可以减少内存的消耗,提高性能(这个事件池就是比如单击事件SyntheticMouseEvent、焦点事件SyntheticFocusEvent的集合)。
映射表结构
{
click: { key1: fn1, key2: fn2 },
change: { key1: fn1, key2: fn2 }
}
对于同一个事件类型,不管注册几次,最终只会保留一个有效实例(document.addEventListener('click', dispatchEvent))。
阻止原生事件冒泡合成事件不能执行,阻止合成事件冒泡不影响原生事件。
原生事件先执行,合成事件后执行。子原生事件先执行,父原生事件先执行,子合成事件后执行,父合成事件后执行。
阻止冒泡的方法只能通过event.preventDefault()来阻止,不能用event.stopPropagation()和event.cancelBubble = false和return false(return false在js中只能阻止默认行为,只有在jquery才能阻止默认行为和事件冒泡)。
合成事件与原生事件的区别:
1.封装事件,抹平不同浏览器的差异。
2.写法不同,合成事件是用驼峰式写法,原生事件是用全部小写。
3.事件处理的语法不同,合成事件是用函数的形式,原生事件是用字符串的形式。
4.阻止冒泡的方法不同,合成事件是用event.preventDefault()来阻止,原生事件是用event.stopPropagation()和event.cancelBubble = false和return false来阻止。
无法在异步中访问参数e,因为参数e也是合成事件封装的,不是原生事件的参数e。
不是所有的原生事件都有合成事件,比如audio、video的onplay、onpause,这些事件是用原生标签进行代理,但仍由dispatchEvent进行绑定。

react fiber
提出fiber的原因是因为传统react递归渲染是不可中断的,一旦开始就要等到执行完才能结束,这对于那种很深的dom结构将会产生丢帧的情况。于是提出了fiber,fiber是一种链表,用来模拟传统的js调用栈,区别于传统js调用栈,这种模拟的调用栈可以中断执行,每一个fiber节点都有dom节点实例stateNode,子节点child,兄弟节点sibling,父节点return,fiber是增量渲染,将每一个渲染任务分布到每一帧中,fiber分为两个阶段,分别是协调阶段和提交阶段,协调阶段可以中断,提交阶段不能中断,diff就发生在协调阶段中。fiber通过虚拟dom来进行创建,父节点之和第一个子节点建立父子关系,子节点通过sibling建立兄弟关系,通过return建立父子关系,并且将虚拟dom上的属性作为自己的属性。在diff过程中,获取当前的fiber节点和fiber的子节点作为next节点,通过当前fiber树和新的虚拟dom,层次递归的进行对比,有三种情况,分别是element节点,文本节点,数组节点,在element节点中,如果key值不同,那么删除当前节点保留兄弟节点,因为兄弟节点可能能被复用,如果key相同,type不同,那么删除当前节点和兄弟节点,如果key和type都相同,那么复用当前节点,在文本节点中,如果旧的节点是文本节点,那么就复用,如果旧的节点不是文本节点,那么就删除当前节点和兄弟节点,创建新的文本节点,在数组节点中,比较oldFiber.index和newIndx,如果oldFiber.index大于newIndex,那么就将oldFiber赋值给nextOldFiber,否则将oldFiber的兄弟节点赋值给nextOldFiber,执行updateSlot,复用节点,返回newFiber,如果newFiber为空,那么跳出循环,调用placeChild,返回最新的lastPlacedIndex,如果旧节点全部复用完,还有新节点,那么就创建新节点,如果还有旧节点,那么就用map结构集合起来,看是否还有可以复用的节点,如果有,那么就复用,如果没有,那么就新建。在placeChild这个过程中,对比oldIndex和lastPlacedIndex,如果满足oldIndex小于lastPlacedIndex,那么就打上标记,并且lastPlacedIndex更新为oldIndex和lastPlacedIndex中的最大值。被中断之后再次调用可以通过next节点来快速定位要继续更新的节点,并在更新每一个节点之后判断是否还有剩余时间,如果有就继续更新,没有就将控制权返回给浏览器,更新完后的节点在一次性的提交渲染。

posted on 2022-08-22 01:48  forseize  阅读(37)  评论(0编辑  收藏  举报

导航