ResizeObserver
该接口可以监听到 Element
的内容区域或 SVGElement
的边界框改变,
可以在回调里得到此时元素的尺寸,快速做相应针对布局的操作。比如根据宽度进行一些操作。
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
entry.target.style.borderRadius = Math.max(0, 250 - entry.contentRect.width) + 'px';
}
});
//监听一个元素的布局变化
resizeObserver.observe(document.querySelector('.box:nth-child(2)'));
useLayoutEffect
先来看下该函数的声明。
type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never };
type EffectCallback = () => (void | Destructor);
function useLayoutEffect(
effect: EffectCallback, deps?: DependencyList): void;
react官网对其的描述:
其函数签名与
useEffect
相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect
内部的更新计划将被同步刷新。它会阻塞视觉更新。
它会在浏览器dom渲染之前进行同步执行,即有阻塞渲染的作用。
useLayoutEffect
可以在里面设置对布局更新的监听。 useEffect
是在每次render完成并且完成dom更新以后才会被调用。所以 两者的区别是: useLayoutEffect
是在做实际的dom更新以前执行,类似于,一个团队里面的发版,有一个人说我马上要发版(setstate)了,有没有谁发版,然后useLayoutEffect
定义的effect说我也要发(更新),等一下我(setstate),或者告诉发版的那个人,等你发完后通知我(更新完后通知我 ResizeObserver设置监听) 有点像先知先觉,知道马上要做dom的更新操作了。所以他们会出现在说如果有一个操作改变了state, useEffect
是在dom更新以后被调用,是后知后觉。
例子
import "./styles.css";
import React, { useState, useEffect, useLayoutEffect } from "react";
export default function App() {
let [a, setA] = useState(1);
console.log('render')
useEffect(() => {
console.log('bind')
var p = new ResizeObserver((e) => {
console.log(1, e);
});
return () => {
console.log('unbind')
p.disconnect();
p.unobserve(document.querySelector(".App"));
};
});
useLayoutEffect(() => {
console.log('layout')
var p = new ResizeObserver((e) => {
console.log(2, e);
});
p.observe(document.querySelector(".App"));
return () => {
console.log('unlayout')
p.disconnect();
p.unobserve(document.querySelector(".App"));
};
});
return (
<div className="App" onClick={() => setA(a + 1)}>
<h1>Hello CodeSandbox{a}</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
当点击页面的时候,打印如下:
render
unlayout
layout
unbind
...这里进行dom更新...
bind
2
先留个问题,为什么 没有打印useeffect里面的 1 呢?
下面来分析一下:
刚开始 设置了 useEffect
回调
useLayoutEffect
回调
点击后 ++a;,触发了hooks的重新调用, 所以执行并打印了 render
.
useLayoutEffect
是先知先觉,effect知道马上要进行dom更新了,先执行了LayoutEffectDestructor
,打印出了 unlayout
,在里面设置了对某个元素布局变化的监听。开始执行 LayoutEffect
.等页面dom更新完成以后,触发了上面对布局监听的回调 打印出了2
然后再去执行 useEffect
的 Destructor
,先把上一次对元素布局监听给取消掉,打印出了unbind
,然后再打印bind
设置监听。
useeffect
因为在useeffect里面每一次更新的监听都在该次的组件更新以前被取消了。再在dom更新以后设置监听,所以无法生效。
总结
-
useLayoutEffect
是先知先觉,每次在dom实际更新以前被执行,里面可以再次setstate或者 设置更新布局的监听回调, -
useEffect
后知后觉,在dom更新以后被执行。可以hold住大多数的场景