面试官问我你能实现测量DOM???

简介: 前言大家好我是Fly哥,好久没输出点react的文章,本篇文章经过作者李坤峰授权转发, 为啥要写这篇,还是想起了字节的面试题,「如何写一个自定义测量hook」,当时给出的解决方案不太好,正好看到这篇文章,分享给大家,供大家学习。原生APIMutationObserverMutationObserver 创建一个观察器,提供了对监视DOM树更改的能力。用于监控DOM节点的变化,如 属性变化、子节点增删改、子树的变化等。// 选择需要观察变动的节点const targetNode = document.querySelector('#root');// 观察器的配置(需要观察什

前言



大家好我是Fly哥,好久没输出点react的文章,本篇文章经过作者李坤峰授权转发, 为啥要写这篇,还是想起了字节的面试题,「如何写一个自定义测量hook」,当时给出的解决方案不太好,正好看到这篇文章,分享给大家,供大家学习。


原生API


MutationObserver


MutationObserver 创建一个观察器,提供了对监视DOM树更改的能力。用于监控DOM节点的变化,如 属性变化、子节点增删改、子树的变化等。


// 选择需要观察变动的节点
const targetNode = document.querySelector('#root');
// 观察器的配置(需要观察什么变动)
const config = {
  attributes: true, // 观察属性变动
  childList: true, // 观察目标子节点的变化,是否有添加或者删除
  subtree: true,// 观察后代节点,默认为 false
  attributeFilter: ['style'] // 过滤需观察的属性
};
// 当观察到变动时执行的回调函数
const callback = function(mutationsList, observer) {
  // mutationsList 发生变化dom list
  for(let mutation of mutationsList) {
    if (mutation.type === 'childList') {
     console.log('A child node has been added or removed.');
    }
    else if (mutation.type === 'attributes') {
     console.log('The ' + mutation.attributeName + ' attribute was modified.');
    }
  }
};
// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);
// 以上述配置开始观察目标节点
observer.observe(targetNode, config);
// 之后,可停止观察
observer.disconnect();


ResizeObserver


ResizeObserver 创建一个新的ResizeObserver对象监听元素大小变化。专门用来观察DOM元素的尺寸是否发生了变化。


// 选择需要观察变动的节点
const targetNode = document.querySelector('#root');
// 创建一个观察器实例
const resizeObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
   entry.target.style.borderRadius = Math.max(0, 250 - entry.contentRect.width) + 'px';
  }
});
// 开始观察目标节点
resizeObserver.observe(targetNode);
// 取消全部元素监听
resizeObserver.disconnect()


react中监测dom元素大小变化


useSize


ahooks中的useSize封装了ResizeObserver,实现了对dom元素尺寸变化的监测


const size = useSize(target);


问题


以上API,无论是MutationObserver 还是ResizeObserver,仅支持静态dom的监听,即监听时此dom已存在。如果需要监测一个动态渲染的dom,则必须在元素渲染完成后实例化监测方法。在vue中,我们可以使用$nextTick保证dom加载完成后执行实例化方法,那么在react中,如何知道一个元素已经渲染完成呢?在react官方文档中给出了答案,我该如何测量 DOM 节点?useRef返回一个可变的 ref 对象,但它并不会把当前 ref 的值的 变化 通知到我们。获取 DOM 节点的位置或是大小的基本方式是使用 callback ref,它将在组件挂载和卸载时通知我们。


function MeasureExample() {
  // 记录dom高度
  const [height, setHeight] = useState(0);
  // dom ready
  const measuredRef = useCallback(node => {    
      if (node !== null) {      
          setHeight(node.getBoundingClientRect().height);    
      }  
  }, []);
  // 组件
  return (
    <>
      <h1 ref={measuredRef}>Hello, world</h1>
      <h2>The above header is {Math.round(height)}px tall</h2>
    </>
  );
}


在react中监测动态dom大小变化



Callback ref监测初始化时dom高度 + useSize监测dom变化,覆盖dom变化的全场景


let docHeight = 0// 记录dom高度
function MeasureExample() {
    //  强制render,初始化dom监听
    const [, setDomReady] = useState(false)
    //  高度变化统一处理
    const changeHeight = (h) => {
        //  height=0,return
        if (!h || docHeight === h) return
        //  dom高度发生变化
        //  TODO
    }
    // dom ready
    const measuredRef = useCallback(node => {    
        if (node !== null) {
            //  dom初始化获取高度
            changeHeight(node.getBoundingClientRect().height)
            //  强制刷新,useSize绑定dom
            setDomReady(true) 
        }
    }, [changeHeight]);
    //  实例化dom监听
    const size = useSize(document.querySelector('#target'))
    useEffect(() => {
        //  dom大小发生变化
        changeHeight(size.height)
    }, [changeHeight, size])
    // 组件
    return (
        <>
              { loading ? null : (
                  <h1 id="target" ref={measuredRef}>Hello, world</h1> 
              )}
              <h2>The above header is {Math.round(height)}px tall</h2>
        </>
    );
}
相关文章
|
4月前
|
移动开发 JavaScript 前端开发
【Vue面试题二十二】、什么是虚拟DOM?如何实现一个虚拟DOM?说说你的思路
这篇文章深入探讨了虚拟DOM的概念、必要性以及在Vue中的实现方式,解释了虚拟DOM如何作为真实DOM的轻量级抽象,通过优化DOM操作提高性能,并实现跨平台渲染的能力。
【Vue面试题二十二】、什么是虚拟DOM?如何实现一个虚拟DOM?说说你的思路
|
3月前
|
机器学习/深度学习 JavaScript 算法
面试中的网红虚拟DOM,你知多少呢?深入解读diff算法
该文章深入探讨了虚拟DOM的概念及其diff算法,解释了虚拟DOM如何最小化实际DOM的更新,以此提升web应用的性能,并详细分析了diff算法的实现机制。
|
7月前
|
XML JavaScript 前端开发
【面试题】面试官:谈谈你知道的DOM常见的操作
【面试题】面试官:谈谈你知道的DOM常见的操作
208 0
|
7月前
|
Web App开发 JavaScript 安全
【面试题】 阿里面试官:请设计一个不能操作DOM和调接口的环境
【面试题】 阿里面试官:请设计一个不能操作DOM和调接口的环境
|
JavaScript 前端开发 调度
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-fiber和虚拟dom关系
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-fiber和虚拟dom关系
85 0
|
开发框架 JavaScript 前端开发
web前端面试高频考点——Vue原理(理解MVVM模型、深度/监听data变化、监听数组变化、深入了解虚拟DOM)
web前端面试高频考点——Vue原理(理解MVVM模型、深度/监听data变化、监听数组变化、深入了解虚拟DOM)
250 0
|
缓存 JavaScript 前端开发
web前端面试高频考点——JavaScript-Web-API 篇(一)DOM、BOM、事件
web前端面试高频考点——JavaScript-Web-API 篇(一)DOM、BOM、事件
117 0
|
机器学习/深度学习 JavaScript 算法
面试中的网红虚拟DOM,你知多少呢?深入解读diff算法
众所周知,在前端的面试中,面试官非常爱考vdom和diff算法。比如,可能会出现在以下场景🤏
面试中的网红虚拟DOM,你知多少呢?深入解读diff算法
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。