問題現象
在實際項目開發中,我們常常會遇到類似如下頁面崩潰的問題,導致瀏覽器頁面崩潰的原因一般都是JS Heap堆內存溢出,但此類問題一般控制枱都不會報錯,所以其定位問題的經驗和手段很關鍵,下文是我在實際項目中遇到問題的總結。
問題定位
首先,説一下項目技術棧背景,是採用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)執行情況
我們可以明顯發現getParentClassName方法死循環了,JS Heap從開始的28.3M上升到747M,方法執行耗時佔比84.9%。這種定位方法比手工斷點的定位方法會快出很多,很值得各位同學借鑑。