MST
星途 面试题库

面试题:React 性能调优时如何结合生命周期规避内存泄漏

在 React 应用中,随着组件的频繁创建和销毁,如果处理不当很容易产生内存泄漏。请结合 React 生命周期,说明在哪些生命周期方法中可能会引入内存泄漏问题,以及如何避免这些问题以实现更好的性能优化?
23.4万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

可能引入内存泄漏问题的生命周期方法

  1. componentDidMount:在此方法中常进行一些副作用操作,如添加事件监听器、订阅消息等。若在组件销毁时没有清理这些操作,就会导致内存泄漏。例如:
import React, { Component } from'react';

class MemoryLeakComponent extends Component {
    componentDidMount() {
        window.addEventListener('scroll', this.handleScroll);
    }

    handleScroll = () => {
        console.log('Window scrolled');
    }

    render() {
        return <div>Component with potential memory leak</div>;
    }
}

export default MemoryLeakComponent;

在上述代码中,componentDidMount 里添加了 windowscroll 事件监听器,但未清理。

  1. componentWillUpdate:虽然该方法已被废弃(在 React 17+ 中使用 getSnapshotBeforeUpdate 替代),在旧版本中若在此方法中进行一些未清理的异步操作,如启动定时器等,且在组件销毁时未清除定时器,也会引发内存泄漏。例如:
import React, { Component } from'react';

class OldLeakComponent extends Component {
    componentWillUpdate() {
        this.timer = setInterval(() => {
            console.log('Timer running');
        }, 1000);
    }

    render() {
        return <div>Old component with potential leak</div>;
    }
}

export default OldLeakComponent;

避免内存泄漏及性能优化的方法

  1. 针对 componentDidMount 中的副作用操作:在 componentWillUnmount 生命周期方法中清理相应操作。例如,对于前面添加的 scroll 事件监听器,修改如下:
import React, { Component } from'react';

class FixedMemoryLeakComponent extends Component {
    handleScroll = () => {
        console.log('Window scrolled');
    }

    componentDidMount() {
        window.addEventListener('scroll', this.handleScroll);
    }

    componentWillUnmount() {
        window.removeEventListener('scroll', this.handleScroll);
    }

    render() {
        return <div>Component without memory leak</div>;
    }
}

export default FixedMemoryLeakComponent;
  1. 针对异步操作(如定时器):在 componentWillUnmount 中清除定时器。对于前面的定时器示例,修改如下:
import React, { Component } from'react';

class FixedOldLeakComponent extends Component {
    componentWillUpdate() {
        this.timer = setInterval(() => {
            console.log('Timer running');
        }, 1000);
    }

    componentWillUnmount() {
        if (this.timer) {
            clearInterval(this.timer);
        }
    }

    render() {
        return <div>Old component without leak</div>;
    }
}

export default FixedOldLeakComponent;
  1. 使用 React Hooks 时:对于副作用操作,可以使用 useEffect 钩子。useEffect 的第二个参数可以传入依赖数组,若依赖数组为空,其效果类似 componentDidMount。并且可以在返回函数中进行清理操作,类似 componentWillUnmount。例如:
import React, { useEffect } from'react';

const HookComponent = () => {
    const handleScroll = () => {
        console.log('Window scrolled');
    }

    useEffect(() => {
        window.addEventListener('scroll', handleScroll);
        return () => {
            window.removeEventListener('scroll', handleScroll);
        };
    }, []);

    return <div>Hook component without memory leak</div>;
};

export default HookComponent;