React组件卸载后更新state引发内存泄漏问题排查求助
React轮播组件内存泄漏问题修复及性能优化方案
内存泄漏核心问题&修复步骤
你代码的问题根源是把函数组件useEffect的生命周期逻辑错误用到了类组件上,还有几个细节疏漏:
- 类组件的
componentDidMount返回函数不会被自动执行,你写的() => mounted = false清理逻辑完全没有触发,定时器也从来没有被清理,组件卸载后定时器依然在运行调用setState - 你只在创建定时器前判断了
mounted状态,定时器内部执行setState时没有加判断,就算mounted逻辑生效也拦不住卸载后的状态更新 - 变量名大小写不统一:state中定义的是
intervalID(大写D),存储定时器ID时写的是intervalId(小写d),后续根本找不到正确的定时器ID做清理 - 遍历context时用了
map但没有返回值,属于不规范写法,改成forEach更合适
修复后的完整代码如下:
import React, {Component} from 'react' import { Link } from 'react-router-dom' import MoviesContext from '../contexts/Movies' /* Todo: - Work on the transition between movies - Work on sending the user to the right place on book now button press */ class Home extends Component { static contextType = MoviesContext // 把mounted标记改成实例属性,方便生命周期访问 _isMounted = false state = { intervalId:0, // 统一变量名,全小写d index:1, prevIndex:0, nextIndex:2, picList:[] } componentDidMount() { this._isMounted = true const posterURLS = [] // 把没用的map改成forEach this.context.forEach((movies) => { posterURLS.push(movies.posterURL) }) this.setState({picList: posterURLS}) const newIntervalId = setInterval(() => { // 每次更新state前先判断组件是否挂载 if(!this._isMounted) return if (this.state.nextIndex + 1 === this.state.picList.length ){ this.setState({ prevIndex: this.state.index, index: this.state.nextIndex, nextIndex: 0 }) } else { this.setState({ prevIndex: this.state.index, index: this.state.nextIndex, nextIndex: this.state.nextIndex + 1 }) } }, 5000); this.setState({ intervalId: newIntervalId }); } // 新增卸载生命周期,清理定时器+更新挂载标记 componentWillUnmount() { this._isMounted = false clearInterval(this.state.intervalId) } render() { return ( <div className="font-sans font-light text-theme-white text-4xl z-10 flex justify-center items-center h-screen"> <div className="h-3/4"> <div className="h-full justify-center items-center"> <div className="h-full hidden md:flex"> {/* 加懒加载属性,非首屏图片延后加载 */} <img src={this.state.picList[this.state.prevIndex] || ''} alt="this is a movie" className="h-full rounded-2xl -mx-20" loading="lazy"/> <img src={this.state.picList[this.state.nextIndex] || ''} alt="this is a movie" className="h-full rounded-2xl -mx-20" loading="lazy"/> </div> <div className="absolute inset-10 flex justify-center items-center h-screen"> <img src={this.state.picList[this.state.index] || ''} alt="this is a movie" className="h-3/4 rounded-2xl"/> </div> </div> <div className="absolute inset-0 top-10 flex justify-center items-center"> <Link to="/book" className="bg-theme-light text-theme-black rounded-2xl py-3 px-5 hover:bg-theme-black hover:text-theme-light">Book Now</Link> </div> </div> </div> ) } } export default Home
性能优化建议
图片加载优化
- 把所有海报图转成WebP格式,同视觉质量下体积比JPG/PNG小50%以上,能大幅降低加载耗时
- 按页面实际展示尺寸裁剪图片,不要直接上传未压缩的原图,比如轮播图最大展示高度为屏幕高度的3/4,按1080p的展示规格裁剪即可
- 上述代码已经给非当前展示的图片加了
loading="lazy"懒加载属性,能避免首次加载时一次性请求所有海报图
首屏加载优化
- 给Firebase存储的静态资源配置CDN缓存规则,设置合理的缓存时间,避免用户每次访问都重新拉取相同资源
- 电影列表这类不频繁更新的数据做本地缓存,首次拉取后存在localStorage里,后续用户访问先读本地缓存渲染页面,再后台请求最新数据更新,不用等接口返回才渲染
- 给首屏需要展示的前3张海报加预加载配置,在页面head中添加
<link rel="preload" as="image" href="首屏海报地址">,能提前触发图片加载,提升首屏渲染速度
内容的提问来源于stack exchange,提问作者Toby Clark
相关产品推荐
相关产品推荐

