说说对 React 中类组件和函数组件的理解?有什么区别?
问题解析(面试官考察点)
面试官通过此问题主要考察:
- 对 React 两种组件写法的掌握程度
- 对 Hooks 的理解和应用
- 对 this 绑定问题的理解
- 能否根据场景选择合适的组件写法
核心概念(基础知识点)
类组件(Class Component)
通过 ES6 类的形式编写,必须继承 React.Component:
- 可以定义自己的 state
- 可以使用生命周期方法
- 通过
this访问 props 和 state
函数组件(Function Component)
通过函数的形式编写,是 React 中定义组件最简单的方式:
- 早期是无状态组件
- Hooks 出现后可以使用 state 和生命周期
- 没有
this,通过参数访问 props
详细解答(代码示例)
类组件示例
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
handleClick = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<h1>Hello, {this.props.name}</h1>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>+1</button>
</div>
);
}
}
函数组件示例
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 使用 Hooks 的函数组件
import React, { useState, useEffect } from 'react';
function Welcome({ name }) {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component did mount or update');
return () => {
console.log('Component will unmount');
};
}, []);
return (
<div>
<h1>Hello, {name}</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
深入理解(原理剖析)
主要区别对比
| 特性 | 类组件 | 函数组件 |
|---|---|---|
| 编写形式 | ES6 类 | 函数 |
| 状态管理 | this.state / this.setState | useState Hook |
| 生命周期 | 完整的生命周期方法 | useEffect 等 Hooks |
| this 绑定 | 需要处理 this 绑定问题 | 没有 this |
| 代码量 | 相对较多 | 简洁 |
| 性能 | 需要实例化 | 无实例化开销 |
| 逻辑复用 | HOC / render props | 自定义 Hooks |
状态管理对比
类组件:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState(prevState => ({
count: prevState.count + 1
}));
};
render() {
return (
<div>
<p>{this.state.count}</p>
<button onClick={this.increment}>+1</button>
</div>
);
}
}
函数组件:
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(prevCount => prevCount + 1);
};
return (
<div>
<p>{count}</p>
<button onClick={increment}>+1</button>
</div>
);
}
生命周期对比
类组件生命周期:
class Example extends React.Component {
componentDidMount() {
console.log('组件挂载');
}
componentDidUpdate(prevProps, prevState) {
console.log('组件更新');
}
componentWillUnmount() {
console.log('组件卸载');
}
render() {
return <div>Example</div>;
}
}
函数组件 useEffect:
function Example() {
useEffect(() => {
console.log('组件挂载或更新');
return () => {
console.log('组件卸载或依赖变化');
};
}, []); // 空数组表示只在挂载和卸载时执行
return <div>Example</div>;
}
调用方式的区别
函数组件调用:
// 你的代码
function SayHi() {
return <p>Hello, React</p>;
}
// React 内部
const result = SayHi(props); // » <p>Hello, React</p>
类组件调用:
// 你的代码
class SayHi extends React.Component {
render() {
return <p>Hello, React</p>;
}
}
// React 内部
const instance = new SayHi(props); // » SayHi {}
const result = instance.render(); // » <p>Hello, React</p>
获取渲染值的重要区别
函数组件(闭包特性):
function ProfilePage(props) {
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return <button onClick={handleClick}>Follow</button>;
}
类组件(this 可变):
class ProfilePage extends React.Component {
showMessage() {
alert('Followed ' + this.props.user);
}
handleClick() {
setTimeout(() => this.showMessage(), 3000);
}
render() {
return <button onClick={() => this.handleClick()}>Follow</button>;
}
}
关键区别:
- 函数组件:props 是函数参数,不会变化(闭包捕获)
- 类组件:this.props 是可变的,可能获取到最新的值而非点击时的值
最佳实践
优先使用函数组件
-
代码更简洁
- 没有 class 和 this
- 逻辑更清晰
-
更容易测试
- 纯函数更容易测试
- 没有复杂的实例化过程
-
逻辑复用更方便
- 自定义 Hooks
- 比 HOC 和 render props 更直观
自定义 Hooks 示例
// 自定义 Hook
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}
// 使用自定义 Hook
function MyComponent() {
const width = useWindowWidth();
return <div>Window width: {width}</div>;
}
何时使用类组件
虽然函数组件是推荐做法,但以下情况可能需要类组件:
- 维护旧代码
- 需要使用
getSnapshotBeforeUpdate等特定生命周期 - 错误边界(Error Boundaries)
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
面试要点
-
主要区别总结
- 语法:类 vs 函数
- 状态:this.state vs useState
- 生命周期:生命周期方法 vs useEffect
- this:需要绑定 vs 没有 this
-
Hooks 的优势
- 代码复用更简单(自定义 Hooks)
- 逻辑聚合(相关逻辑放在一起)
- 没有 this 绑定问题
-
闭包 vs this
- 函数组件使用闭包捕获 props/state
- 类组件的 this 总是指向最新实例
- 异步操作中可能产生不同行为
-
性能考虑
- 函数组件没有实例化开销
- React.memo 可以实现类似 PureComponent 的效果
- useMemo/useCallback 可以优化性能
-
常见面试题
- 函数组件和类组件的区别?
- 为什么推荐使用函数组件?
- Hooks 能否完全替代类组件?
- 函数组件中的闭包陷阱是什么?
-
现代 React 趋势
- React 团队推荐使用函数组件
- 新特性优先支持 Hooks
- 类组件不会被移除,但不再新增特性