Next.js路由点击后加载延迟、页面崩溃问题及代码排查求助
问题原因
你提供的代码核心问题是没有在组件卸载时清除路由事件监听器,属于典型的事件绑定类内存泄漏:
- 这段代码大概率写在组件的挂载生命周期(比如React的
componentDidMount或者useEffect的回调里),每次组件被挂载时,都会给Router.events新增3个事件监听函数 - 这些监听函数是匿名箭头函数,内部持有当前组件实例的引用,只要监听不解除,组件实例就无法被垃圾回收(GC),内存占用会随着路由跳转次数持续上涨
- 每次路由事件触发时,所有历史注册的监听函数都会同时执行,会批量触发N次无意义的
setState调用,导致页面卡顿、响应延迟,调用次数过多就会直接崩溃 - 本地和预发环境没问题是因为测试时跳转次数少,泄漏量没达到卡顿阈值,线上用户长时间使用、多次跳转就会暴露问题
修复方案
如果是类组件,在componentWillUnmount生命周期里移除对应的监听:
componentWillUnmount() { Router.events.off('routeChangeStart', this.handleRouteStart) Router.events.off('routeChangeComplete', this.handleRouteComplete) Router.events.off('routeChangeError', this.handleRouteError) } // 挂载时需要把匿名函数提取成实例方法,不然off的时候找不到对应的函数引用 componentDidMount() { const isBrowser = typeof window !== 'undefined' if (isBrowser) { this.setState({ isLoading: false }) } this.handleRouteStart = () => this.setState({ isLoading: true }) this.handleRouteComplete = () => this.setState({ isLoading: false }) this.handleRouteError = () => this.setState({ isLoading: false }) Router.events.on('routeChangeStart', this.handleRouteStart) Router.events.on('routeChangeComplete', this.handleRouteComplete) Router.events.on('routeChangeError', this.handleRouteError) }
如果是函数组件,用useEffect的清理函数处理:
useEffect(() => { const isBrowser = typeof window !== 'undefined' if (isBrowser) { setIsLoading(false) } const handleRouteStart = () => setIsLoading(true) const handleRouteComplete = () => setIsLoading(false) const handleRouteError = () => setIsLoading(false) Router.events.on('routeChangeStart', handleRouteStart) Router.events.on('routeChangeComplete', handleRouteComplete) Router.events.on('routeChangeError', handleRouteError) // 清理函数,组件卸载/依赖更新时执行 return () => { Router.events.off('routeChangeStart', handleRouteStart) Router.events.off('routeChangeComplete', handleRouteComplete) Router.events.off('routeChangeError', handleRouteError) } }, [])
调试建议
- 打开Chrome开发者工具的内存(Memory)面板,先拍一个堆快照,然后多次跳转路由后再拍一个堆快照,对比两个快照里的组件实例、事件监听数量,就能直接看到泄漏的对象
- 用开发者工具的性能(Performance)面板,录制多次路由跳转的执行过程,查看主线程执行耗时,就能看到大量重复的setState调用占比
- 可以在Router事件监听的回调里加console日志,多次跳转后触发路由时看控制台打印的日志数量,就能直观看到监听器重复注册的问题
- 检查是否还有其他全局事件(比如window的resize、scroll事件)没有做解绑处理,这类全局绑定的事件是前端内存泄漏的高发点
内容的提问来源于stack exchange,提问作者MomasVII
相关产品推荐
相关产品推荐

