1. 内存管理
- 类组件原生生命周期:
- 在类组件中,
componentDidMount
中添加的事件监听器等资源,需要在 componentWillUnmount
中手动清理。例如:
class MyClassComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
// 处理滚动逻辑
};
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
render() {
return <div>My Class Component</div>;
}
}
- 如果忘记在
componentWillUnmount
中清理,可能会导致内存泄漏,因为事件监听器依然保持对组件实例的引用,使得组件即使从 DOM 中移除,也无法被垃圾回收机制回收。
- React函数组件模拟生命周期(
useEffect
):
useEffect
通过返回一个清理函数来处理资源清理,更简洁直观。例如:
import React, { useEffect } from'react';
const MyFunctionComponent = () => {
useEffect(() => {
const handleScroll = () => {
// 处理滚动逻辑
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
return <div>My Function Component</div>;
};
- 依赖数组为空时,
useEffect
只在组件挂载和卸载时执行,减少了不必要的重复执行,从某种程度上优化了内存管理。同时,清理函数会在组件卸载或依赖变化时执行,保证资源的及时清理,降低内存泄漏风险。
2. 渲染优化
- 类组件原生生命周期:
shouldComponentUpdate
方法可以通过比较前后 props 和 state 来决定是否需要重新渲染组件,从而进行渲染优化。例如:
class MyClassComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.count!== nextState.count;
}
render() {
return <div>{this.state.count}</div>;
}
}
- 但这种方式需要手动编写复杂的比较逻辑,如果比较逻辑不完善,可能导致不必要的渲染或阻止必要的渲染。
- React函数组件模拟生命周期(
useEffect
):
useEffect
依赖数组可以精准控制副作用的触发时机。例如:
import React, { useEffect, useState } from'react';
const MyFunctionComponent = () => {
const [count, setCount] = useState(0);
useEffect(() => {
// 只在count变化时执行
}, [count]);
return <div>{count}</div>;
};
- 当依赖数组为空时,
useEffect
只在组件挂载和卸载时执行,避免了在不必要的渲染时执行副作用,优化了渲染性能。同时,React.memo
可以用于函数组件的浅比较,类似于类组件的 shouldComponentUpdate
,进一步优化渲染。
3. 依赖追踪
- 类组件原生生命周期:
- 类组件的生命周期方法中,难以直观地追踪依赖关系。例如在
componentDidUpdate
中,可能需要手动对比前后 props 和 state 来确定哪些值发生了变化从而触发特定逻辑,代码逻辑复杂且容易出错。
class MyClassComponent extends React.Component {
componentDidUpdate(prevProps, prevState) {
if (prevProps.value!== this.props.value) {
// 处理逻辑
}
}
render() {
return <div>{this.props.value}</div>;
}
}
- React函数组件模拟生命周期(
useEffect
):
useEffect
通过依赖数组明确指定依赖关系,代码更加清晰。例如:
import React, { useEffect, useState } from'react';
const MyFunctionComponent = () => {
const [value, setValue] = useState('');
useEffect(() => {
// 只在value变化时执行
}, [value]);
return <div>
<input value={value} onChange={(e) => setValue(e.target.value)} />
</div>;
};
- 这种方式使得依赖关系一目了然,便于理解和维护代码,减少因依赖不清晰导致的意外副作用执行。
4. 性能更优场景
- 函数组件模拟生命周期性能更优场景:
- 简单副作用场景:如获取数据、添加简单事件监听器等。例如一个简单的定时器功能:
import React, { useEffect } from'react';
const TimerComponent = () => {
useEffect(() => {
const id = setInterval(() => {
console.log('Timer is running');
}, 1000);
return () => {
clearInterval(id);
};
}, []);
return <div>Timer Component</div>;
}
- 依赖明确场景:当副作用依赖少量的 props 或 state 时,通过
useEffect
的依赖数组可以精准控制副作用执行,减少不必要的计算。例如一个根据 URL 参数获取特定数据的组件:
import React, { useEffect, useState } from'react';
import { useLocation } from'react-router-dom';
const DataComponent = () => {
const location = useLocation();
const [data, setData] = useState(null);
useEffect(() => {
const param = new URLSearchParams(location.search).get('id');
// 根据param获取数据并设置到state
}, [location.search]);
return <div>{data}</div>;
};
- 类组件生命周期更具优势场景:
- 复杂状态管理场景:当组件有复杂的状态转换逻辑和大量的状态依赖时,类组件的生命周期和实例方法可以更好地组织代码。例如一个具有多种状态和复杂交互的表单组件,类组件可以通过实例方法封装状态处理逻辑,在不同生命周期方法中调用,使代码结构更清晰。
- 遗留代码迁移场景:在将大量类组件的遗留项目逐步迁移时,对于一些已经高度依赖类组件生命周期的复杂组件,暂时保留类组件形式可以减少重构成本。