React 函数组件和类组件的区别
1. 定义方式
- 函数组件:使用 ES6 的函数定义组件,接收 props 作为参数,返回一个 JSX 元素或 null。
function Welcome(props) {return <h1>Hello, {props.name}</h1>;
}
- 类组件:使用 ES6 的类定义组件,继承自 React.Component,通过 render 方法返回 JSX。
class Welcome extends React.Component {render() {return <h1>Hello, {this.props.name}</h1>;}
}
2. 状态管理
- 函数组件:在 React 16.8 之前,函数组件没有自己的状态,需要通过外部状态管理库(如 Redux)或父组件传值来管理状态。React 16.8 引入了 Hooks 后,函数组件可以通过 useState Hook 管理自己的状态。
import React, { useState } from 'react';function Counter() {const [count, setCount] = useState(0);return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);
}
- 类组件:通过 this.state 来管理组件的内部状态,并使用 this.setState 方法来更新状态。
class Counter extends React.Component {constructor(props) {super(props);this.state = { count: 0 };}render() {return (<div><p>You clicked {this.state.count} times</p><button onClick={() => this.setState({ count: this.state.count + 1 })}>Click me</button></div>);}
}
3. 生命周期方法
- 函数组件:没有生命周期方法,但在 React 16.8 引入 Hooks 后,可以通过 useEffect Hook 来实现类似生命周期的功能。
import React, { useState, useEffect } from 'react';function DataFetching() {const [data, setData] = useState([]);useEffect(() => {fetch('https://api.example.com/data').then(response => response.json()).then(data => setData(data));}, []); // 空数组表示类似 componentDidMount 的效果return (<div>{data.map(item => (<div key={item.id}>{item.name}</div>))}</div>);
}
- 类组件:拥有多个生命周期方法,如 componentDidMount、componentDidUpdate、componentWillUnmount 等,这些方法在组件的挂载、更新和卸载过程中自动调用。
class DataFetching extends React.Component {constructor(props) {super(props);this.state = { data: [] };}componentDidMount() {fetch('https://api.example.com/data').then(response => response.json()).then(data => this.setState({ data }));}render() {return (<div>{this.state.data.map(item => (<div key={item.id}>{item.name}</div>))}</div>);}
}
- 类组件:拥有多个生命周期方法,如 componentDidMount、componentDidUpdate、componentWillUnmount 等,这些方法在组件的挂载、更新和卸载过程中自动调用。
class DataFetching extends React.Component {constructor(props) {super(props);this.state = { data: [] };}componentDidMount() {fetch('https://api.example.com/data').then(response => response.json()).then(data => this.setState({ data }));}render() {return (<div>{this.state.data.map(item => (<div key={item.id}>{item.name}</div>))}</div>);}
}
4. 组件的复用性
- 函数组件:更易于复用逻辑,可以通过自定义 Hooks 来提取组件逻辑,实现逻辑的复用。
function useCounter(initialCount) {const [count, setCount] = useState(initialCount);const increment = () => setCount(count + 1);return { count, increment };
}function Counter() {const { count, increment } = useCounter(0);return (<div><p>You clicked {count} times</p><button onClick={increment}>Click me</button></div>);
}
- 类组件:复用逻辑通常需要借助高阶组件(HOC)或继承的方式,相对来说更加复杂。
5. 性能优化
- 函数组件:可以通过 React.memo 对组件进行性能优化,避免不必要的重渲染。
const MyComponent = React.memo(function MyComponent(props) {/* 只有 props 发生变化时才会重新渲染 */return /* ... */;
});
- 类组件:可以通过 shouldComponentUpdate 方法来控制组件是否更新,从而优化性能。
class MyComponent extends React.Component {shouldComponentUpdate(nextProps, nextState) {return nextProps.value !== this.props.value || nextState.count !== this.state.count;}render() {/* ... */}
}
React.memo 和shouldComponentUpdate 详细讲解
React.memo
React.memo 是 React 提供的一个高阶组件,用于优化函数组件的性能。 当组件的 props 没有发生变化时,它可以阻止组件的不必要的重新渲染。
用法
import React, { memo } from 'react';const MyComponent = (props) => {console.log('MyComponent render', props.value);return <div>{props.value}</div>;
};// 使用 React.memo 包裹组件
const MemoizedMyComponent = memo(MyComponent, (prevProps, nextProps) => {// 自定义比较逻辑return prevProps.value === nextProps.value; // 如果返回 true,则不重新渲染
});// 在父组件中使用
function ParentComponent() {const [count, setCount] = useState(0);return (<div><button onClick={() => setCount(count + 1)}>Update Count</button><MemoizedMyComponent value={count} /></div>);
}
参数说明
- 第一个参数:要被优化的函数组件。
- 第二个参数(可选):一个比较函数,用于比较当前 props 和上一个 props。如果返回 true,组件不会重新渲染;如果返回 false,组件会重新渲染。如果不提供此函数,React 会使用默认的浅比较(shallow comparison)。
默认行为
如果不提供自定义的比较函数,React 会使用浅比较来判断 props 是否发生变化。浅比较会检查对象的引用是否相同,而不是进行深度比较。
const MemoizedMyComponent = memo(MyComponent);
shouldComponentUpdate
shouldComponentUpdate 是类组件中的一个生命周期方法,用于控制组件是否应该在状态或属性发生变化时重新渲染。 如果返回 false,组件不会重新渲染;如果返回 true,组件会重新渲染。
用法
class MyComponent extends React.Component {shouldComponentUpdate(nextProps, nextState) {// 自定义比较逻辑return nextProps.value !== this.props.value || nextState.count !== this.state.count;}render() {console.log('MyComponent render', this.props.value, this.state.count);return <div>{this.props.value}</div>;}
}// 在父组件中使用
function ParentComponent() {const [count, setCount] = useState(0);return (<div><button onClick={() => setCount(count + 1)}>Update Count</button><MyComponent value={count} /></div>);
}
对比
特性 | React.memo | shouldComponentUpdate |
---|---|---|
适用组件类型 | 函数组件 | 类组件 |
使用方式 | 高阶组件,包裹函数组件 | 类组件的生命周期方法 |
参数 | 两个参数:要优化的组件和自定义比较函数(可选) | 两个参数: 和 |
默认行为 | 浅比较 | 无默认行为,需要手动实现比较逻辑 |
性能影响 | 优化函数组件的渲染性能 | 优化类组件的渲染性能 |
适用场景 | 简单的展示型函数组件,或需要根据 props 变化控制渲染的场景 | 复杂的类组件,需要精细控制渲染逻辑的场景 |
代码简洁性 | 代码简洁,易于使用 | 需要手动实现比较逻辑,代码可能较为复杂 |
选择建议
- 如果你使用的是函数组件,建议使用 React.memo 来优化性能。
- 如果你使用的是类组件,建议使用 shouldComponentUpdate 来优化性能。
- 在复杂的业务场景中,可以结合 React.memo 和 shouldComponentUpdate 来实现更精细的性能优化。
6. 代码简洁性
- 函数组件:代码更为简洁,逻辑清晰,尤其是使用 Hooks 后,可以将相关逻辑放在一起,增强可读性。
- 类组件:代码相对复杂,需要处理 this 的指向问题,且生命周期方法分散在不同地方,逻辑可能不够直观。
7. 使用场景
- 函数组件:适用于简单的展示型组件或逻辑较为简单的场景,随着 Hooks 的引入,函数组件的功能得到了极大的增强,现在也被广泛用于复杂的业务场景。
- 类组件:适用于需要管理复杂状态和生命周期的组件,或者在一些特殊的场景下(如需要使用 ref 来访问 DOM 节点)。
总结
特性 | 函数组件 | 类组件 |
---|---|---|
定义方式 | 使用函数定义,接收 参数,返回 JSX | 使用类定义,继承自 ,通过 方法返回 JSX |
状态管理 | React 16.8 之前无状态,16.8 之后通过 Hook 管理状态 | 通过 和 管理状态 |
生命周期方法 | 无生命周期方法,通过 Hook 实现类似功能 | 拥有多个生命周期方法,如 、 等 |
复用逻辑 | 通过自定义 Hooks 复用逻辑 | 通过高阶组件(HOC)或继承复用逻辑 |
性能优化 | 使用 | 使用 |
代码简洁性 | 更为简洁,逻辑清晰 | 代码相对复杂,需处理 指向 |
使用场景 | 简单展示型组件或逻辑简单的场景,现在也适用于复杂业务场景 | 复杂状态管理和生命周期控制的场景,或特殊场景(如需要访问 DOM 节点) |