10

问题现象

在实际项目开发中,我们常常会遇到类似如下页面崩溃的问题,导致浏览器页面崩溃的原因一般都是JS Heap堆内存溢出,但此类问题一般控制台都不会报错,所以其定位问题的经验和手段很关键,下文是我在实际项目中遇到问题的总结。

页面崩溃1.gif

问题定位

首先,说一下项目技术栈背景,是采用React+Mobx+single-react-spa搭建的一个微前端中后台应用。当初始化加载完页面,点击”删除“触发事件后页面就卡死,过一会儿直接崩溃,但是控制台没有任何报错信息,定位问题无从下手。思考片刻后,决定采用最基本的方法试一试,删除事件会删除表格源数据,会触发表格组件以及其部分子孙组件的重新渲染,在每一个会重新渲染的组件以及组件内部定义和使用的函数体中加上断点,最后断点调试发现getParentClassName方法体内部陷入死循环,当currentElement=null时,while循环体就陷入死循环

/** 获取所有父节点的className */
export const getParentClassName = (element: Element, fatherClassName?: string) => {
  const classNames = [];
  let currentElement = element;

  classNames.push(currentElement?.className || '');

  if (fatherClassName) {
    while (
      !(currentElement?.className || '').includes(fatherClassName) &&
      currentElement !== document.body
    ) {
      classNames.push(currentElement?.className || '');
      currentElement = currentElement?.parentElement as Element;
    }
  } else {
    while (currentElement !== document.body) {
      classNames.push(currentElement?.className || '');
      currentElement = currentElement?.parentElement as Element;
    }
  }

  classNames.push(currentElement?.className || '');

  return classNames;
};

因为无法定位引发问题的代码块,上面调试方法就像黑盒测试一样,比较消耗时间。后来想了一下,其实我们可以使用performance记录整个触发删除事件后的栈帧(Stack Frame)执行情况

image-20220226155912563.png

image-20220226160032937.png

image-20220226160125088.png

我们可以明显发现getParentClassName方法死循环了,JS Heap从开始的28.3M上升到747M,方法执行耗时占比84.9%。这种定位方法比手工断点的定位方法会快出很多,很值得各位同学借鉴。


记得要微笑
1.9k 声望4.5k 粉丝

知不足而奋进,望远山而前行,卯足劲,不减热爱。


引用和评论

0 条评论