文章目录
React 的类组件和函数式组件是构建用户界面的两种主要方式,它们各有特点和适用场景。下面从 8 个关键维度进行详细对比分析。
一、基本语法差异
1. 类组件 (Class Components)
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<button onClick={this.handleClick}>
点击次数: {this.state.count}
</button>
);
}
}
2. 函数式组件 (Function Components)
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<button onClick={handleClick}>
点击次数: {count}
</button>
);
}
二、核心差异对比
特性 | 类组件 | 函数式组件 |
---|---|---|
语法复杂度 | 需要更多样板代码 | 语法更简洁 |
状态管理 | this.state 和 setState | useState/useReducer Hook |
生命周期 | 生命周期方法 | useEffect Hook |
this绑定 | 需要手动绑定 | 无需绑定 |
性能优化 | PureComponent/shouldComponentUpdate | React.memo + useMemo/useCallback |
代码复用 | HOC/Render Props | 自定义Hooks |
未来兼容性 | 仍支持但不再推荐新代码使用 | React官方推荐方式 |
测试难度 | 相对复杂 | 更易测试 |
三、生命周期对应关系
类组件生命周期 → Hooks 替代方案
类组件生命周期 | 函数组件等价实现 |
---|---|
constructor | useState初始化 |
componentDidMount | useEffect(fn, []) |
componentDidUpdate | useEffect(fn) |
componentWillUnmount | useEffect返回的清理函数 |
shouldComponentUpdate | React.memo + useMemo |
getDerivedStateFromProps | useState + useEffect |
getSnapshotBeforeUpdate | 暂无完美替代,需使用refs |
示例对比
// 类组件生命周期
class Example extends React.Component {
componentDidMount() {
console.log('组件挂载');
this.timer = setInterval(() => {}, 1000);
}
componentDidUpdate(prevProps) {
if (this.props.id !== prevProps.id) {
console.log('ID变化');
}
}
componentWillUnmount() {
clearInterval(this.timer);
}
}
// 函数组件等价实现
function Example({ id }) {
useEffect(() => {
console.log('组件挂载');
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer);
}, []);
useEffect(() => {
if (id) {
console.log('ID变化');
}
}, [id]);
}
四、状态管理差异
1. 类组件状态特点
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
user: null,
loading: true
};
}
// 状态更新是合并式
fetchUser() {
this.setState({ loading: true });
fetchUser().then(user => {
this.setState({ user, loading: false });
});
}
}
特点:
- 状态是单一对象
- setState是异步合并更新
- 复杂状态逻辑可能分散在各个生命周期
2. 函数组件状态特点
function User() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
// 状态更新是替换式
const fetchUser = () => {
setLoading(true);
fetchUser().then(data => {
setUser(data);
setLoading(false);
});
};
}
特点:
- 可以拆分多个状态变量
- 状态更新是替换而非合并
- 相关逻辑可以集中组织
五、性能优化方式
1. 类组件优化
// PureComponent浅比较
class List extends React.PureComponent {
render() {
return this.props.items.map(item =>
<Item key={item.id} item={item} />
);
}
}
// 手动控制更新
class Item extends React.Component {
shouldComponentUpdate(nextProps) {
return nextProps.item.id !== this.props.item.id;
}
}
2. 函数组件优化
// React.memo记忆组件
const List = React.memo(({ items }) => (
items.map(item => <Item key={item.id} item={item} />)
);
// useMemo记忆计算结果
function Chart({ data }) {
const formattedData = useMemo(() =>
formatData(data), [data]);
return <ChartCore data={formattedData} />;
}
// useCallback记忆函数
const Item = React.memo(({ item, onClick }) => (
<div onClick={onClick}>{item.name}</div>
));
function List({ items }) {
const handleClick = useCallback(id => {
/* ... */
}, []);
}
六、代码复用机制对比
1. 类组件复用模式
// 高阶组件(HOC)
function withLogger(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log('组件挂载');
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
// Render Props
class DataProvider extends React.Component {
state = { data: null };
render() {
return this.props.children(this.state.data);
}
}
2. 函数组件复用模式
// 自定义Hook
function useLogger(name) {
useEffect(() => {
console.log(`${name} 挂载`);
}, [name]);
}
function MyComponent() {
useLogger('MyComponent');
return <div />;
}
// Hook组合
function useUser(userId) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
return user;
}
七、错误处理方式
1. 类组件错误边界
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
logError(error, info);
}
render() {
return this.state.hasError
? <FallbackUI />
: this.props.children;
}
}
2. 函数组件错误处理
// 目前函数组件不能直接实现错误边界
// 仍需要使用类组件作为边界
// 但可以处理局部错误
function Component() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
fetchData()
.then(setData)
.catch(setError);
}, []);
if (error) return <ErrorDisplay error={error} />;
return <DataView data={data} />;
}
八、React未来发展趋势
-
函数组件是未来:
- React团队推荐新代码使用函数组件+Hooks
- 类组件仍支持但不会有新特性加入
-
并发模式特性:
- Suspense、Transition等新API主要面向函数组件设计
- 类组件兼容但使用不便
-
服务端组件:
- React Server Components主要与函数组件配合
总结与选型建议
何时使用类组件?
- 维护现有类组件代码库
- 需要使用
getSnapshotBeforeUpdate
等特殊生命周期 - 需要错误边界(目前仍需类组件)
何时使用函数组件?
- 所有新开发项目
- 需要更好的代码组织和复用
- 希望使用最新React特性(Hooks、并发模式等)
- 追求更简洁的代码和更好的性能
迁移策略
// 类组件
class Profile extends React.Component {
state = { user: null };
componentDidMount() {
fetchUser().then(user => this.setState({ user }));
}
render() {
return <div>{this.state.user?.name}</div>;
}
}
// 转换为函数组件
function Profile() {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then(setUser);
}, []);
return <div>{user?.name}</div>;
}
无论选择哪种方式,理解两者的差异和各自的优势,才能在不同场景下做出最佳选择。随着React生态的发展,函数组件+Hooks已成为更现代、更推荐的做法。