useLayoutEffect深层剖析
useLayoutEffect
使用
useLayoutEffect 函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。
可以使用它来读取 DOM 布局并同步触发重渲染。
在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
尽可能使用标准的 useLayoutEffect 以避免阻塞视觉更新。
1 |
|
useEffect 和 useLayoutEffect 区别
useLayoutEffect 会阻塞浏览器渲染真实 DOM【真实 DOM 对象已经创建了】,优先执行 Effect 链表中的 callback;
useEffect 不会阻塞浏览器渲染真实 DOM,在渲染真实 DOM 的同时,去执行 Effect 链表中的 callback
- 它们里面的回调函数都是放在 effect 链表中的,但是 useLayoutEffect 设置的 callback 要优先于 useEffect 去执行
- 在两者设置的 callback 中,依然可以获取 DOM 元素「原因:真实 DOM 对象已经创建了,区别只是浏览器是否渲染」
- 如果在 callback 函数中又修改了状态值「视图又要更新」
- useEffect:浏览器肯定是把第一次的真实已经绘制了,再去渲染第二次真实 DOM【频繁切换有闪烁】
- useLayoutEffect:浏览器是把两次真实 DOM 的渲染,合并在一起渲染的【频繁切换无闪烁】
视图更新周期:
第一步:基于 babel-preset-react-app 把 JSX 编译为 createElement 格式
第二步:执行 createElement(…)方法,创建出 virtualDOM
第三步:基于 root.render 方法把 virtualDOM 变为真实 DOM 对象「DOM-DIFF」
useLayoutEffect 阻塞浏览器绘制:在整个视图渲染更新周期中,创建出真实 DOM 以后直接执行 useLayoutEffect 的 effect 链表中的方法。如果该方法有 setXXX 操作,那么会直接进入下一次更新周期中,而不会执行第四步。因此无论视图更新的过程执行了多少次,界面永远只看到了一次变化,即【频繁切换无闪烁】
useEffect 不阻塞浏览器绘制:在 React 渲染完真实 DOM 之后、浏览器绘制完毕之前会执行 effect 链表中的方法,同时第四步也会异步执行。如果 effect 链表中的方法有 setXXX 操作,那么会直接进入下一次更新周期中,同时上一次更新周期中的第四步还在执行,浏览器还在重绘。如果在 useEffect 中频繁触发更新,后台会异步运行多个“第四步”,由于浏览器绘制是需要一定时间的,因此对于速度较慢的设备,用户会看到多次重绘之间的“白屏”,即【频繁切换有闪烁】
第四步:浏览器渲染和绘制真实 DOM 对象
从视图更新周期可以看出,useLayoutEffect 和 useEffect 都是可以获取真实 DOM 的时机
官方文档示例参考
https://react.docschina.org/reference/react/useLayoutEffect#usage