React的useLayoutEffect和useEffect的执行时机有什么区别?具有副作用代码的函数。更改DOM、添加订阅、设置计时器、记录日志以及在功能组件主体(此处为React渲染阶段)内执行其他具有副作用的操作是不允许的,因为这可能会导致莫名其妙的错误并破坏UI的一致性。使用useEffect执行副作用。分配给useEffect的函数将在组件呈现到屏幕后执行。您可以将效果视为从React的纯函数世界到命令世界的逃生路线。useLayoutEffect(create,deps):其函数签名与useEffect相同,但会在所有DOM变化后同步调用effect。它可用于读取DOM布局并同步触发重新渲染。useLayoutEffect内部的更新计划会在浏览器执行绘图之前同步刷新。注意粗体字段。React的官方文档其实很清楚的说明了这两个钩子的执行时机。下面我们深入react的执行过程,了解useEffect和useLayoutEffect的区别?useEffect和useLayoutEffect哪个相当于componentDidMount和componentDidUpdate?useEffect和useLayoutEffect哪个相当于componentWillUnmount?为什么建议把修改DOM的操作放在useLayoutEffect而不是useEffect?diff之后,react进程会进入commit阶段,准备将虚拟DOM中的变化映射到真实DOM中。在commit阶段前期,会调用一些生命周期的方法。对于类组件,需要触发组件的getSnapshotBeforeUpdate生命周期,对于函数组件,此时会调度useEffect的createdestroy函数。注意是调度,不是执行。在这个阶段,使用useEffect组件产生的生命周期函数会被列在React自己维护的dispatchqueue中,并赋予正常的优先级,这样这些生命周期函数就可以异步执行//可以大致认为React做了这么一步,在实际过程中要复杂很多。setTimeout(()=>{constpreDestory=element.destroy;if(!preDestory)prevDestroy();constdestroy=create();element.destroy=destroy;},0);然后,就到了React的阶段将虚拟DOM设置为真实DOM。这个阶段主要调用的函数是commitWork。commitWork函数会针对不同的fiber节点调用不同的DOM修改方法,比如文本节点和元素节点的修改方法是不同的。如果commitWork遇到类组件的fiber节点,不会做任何操作,直接返回,完成工作,再处理下一个节点。这很容易理解。类组件的fiber节点没有对应的真实DOM结构,所以没有相关操作,但是hooks可用后,函数组件会在创建过程中同步调用useLayoutEffect(create,deps)create函数返回的destroy函数最后的渲染。请注意,一个节点在commitWokr之后。这时候,我们已经把发生的变化映射到了真实的DOM上,但是由于JS线程和浏览器渲染线程是互斥的,因为JS虚拟机还在运行,即使内存中的真实DOM发生了变化,浏览器不会立即将其呈现到屏幕上。收尾工作会完成,相应的生命周期方法会同步执行。我们说的componentDidMount、componentDidUpdate和useLayoutEffect(create,deps)的create函数都是在这个阶段同步执行的。对于react来说,commit阶段是不能被打断的,所有需要commit的节点都会一次性commit。此时react更新,JS停止执行。浏览器将改变后的DOM渲染到屏幕上,至此react仅以一次回流和重绘为代价,更新了所有需要更新的DOM节点。浏览器渲染完成后,浏览器通知react处于空闲阶段,react开始执行自己调度队列中的任务,然后开始执行useEffect(create,deps)生成的函数来应答useEffect和useLayoutEffect的区别?useEffect在渲染时异步执行,直到浏览器渲染完所有屏幕变化后才会执行。useLayoutEffect在渲染时同步执行,其执行时机与componentDidMount和componentDidUpdate一致。useEffect和useLayoutEffect哪个相当于componentDidMount和componentDidUpdate?useLayoutEffect,因为从源码中的调用位置来看,useLayoutEffect的create函数的调用位置和时机与componentDidMount、componentDidUpdate一致,都是React同步调用的,会阻塞浏览器渲染。详见前端高级面试题,useEffect和useLayoutEffect哪个相当于componentWillUnmount?如上调用useLayoutEffect的detroy函数的位置和时机与componentWillUnmount是一致的,都是同步调用的。useEffect的destroy函数在调用时机上更像是componentDidUnmount(注意React中没有这个生命周期函数)。为什么建议把修改DOM的操作放在useLayoutEffect而不是useEffect?可以看到在9/10的过程中,DOM被修改了,但是浏览器渲染线程还是阻塞的,所以回流和重绘的过程还没有发生。由于修改了内存中的DOM,可以通过useLayoutEffect获取到最新的DOM节点,此时修改DOM的样式。假设修改了元素的高度,这些修改会在第11步和react一起,一次性渲染到屏幕上,仍然只有一次回流和重绘的开销。如果放在useEffect中,组件渲染到屏幕后就会执行useEffect的函数。这时候修改DOM会触发浏览器再次回流和重绘,增加了性能的损失。
