React 构建组件的方式有哪些?区别?
问题解析(面试官考察点)
面试官通过此问题主要考察:
- 对 React 组件发展历史的了解
- 对不同组件写法的掌握
- 能否根据场景选择合适的组件类型
- 对现代 React 最佳实践的理解
核心概念(基础知识点)
组件化的优势
组件化开发是现代前端开发的核心理念:
- 降低耦合度:保持接口不变的情况下,可以替换不同组件
- 调试方便:通过排除法快速定位问题
- 提高可维护性:每个组件职责单一,逻辑简单
- 代码复用:组件可在多个场景重复使用
三种构建方式
- 函数式创建:简单、无状态(Hooks 之前)
- React.createClass:早期推荐方式(已废弃)
- 继承 React.Component:类组件方式
详细解答(代码示例)
方式一:函数式创建(推荐)
// 基础函数组件
function HelloComponent(props) {
return <div>Hello {props.name}</div>;
}
// 使用箭头函数
const HelloComponent = (props) => {
return <div>Hello {props.name}</div>;
};
// 解构 props
const HelloComponent = ({ name, age }) => {
return <div>Hello {name}, you are {age}</div>;
};
// 使用 Hooks 的函数组件
import React, { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>Seconds: {seconds}</div>;
}
方式二:React.createClass(已废弃)
// 这种写法已经不再推荐使用
var HelloComponent = React.createClass({
getInitialState: function() {
return { count: 0 };
},
handleClick: function() {
this.setState({ count: this.state.count + 1 });
},
render: function() {
return (
<div onClick={this.handleClick}>
Count: {this.state.count}
</div>
);
}
});
注意: React 15.5 开始已废弃,React 16 彻底移除。
方式三:继承 React.Component
import React from 'react';
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(state => ({
seconds: state.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div>
Seconds: {this.state.seconds}
</div>
);
}
}
深入理解(原理剖析)
三种方式对比
| 特性 | 函数组件 | React.createClass | 类组件 |
|---|---|---|---|
| 语法 | 函数 | 对象配置 | ES6 类 |
| 状态 | Hooks | getInitialState | constructor |
| this 绑定 | 无 this | 自动绑定 | 需要手动绑定 |
| 生命周期 | useEffect | 完整生命周期 | 完整生命周期 |
| 性能 | 好 | 一般 | 需要实例化 |
| 当前状态 | 推荐 | 已废弃 | 可用 |
函数组件的演变
React Hooks 之前(无状态):
function Hello({ name }) {
return <h1>Hello, {name}</h1>;
}
// 只能接收 props,无法管理状态
React Hooks 之后(有状态):
import { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
编译后的代码对比
JSX 代码:
function HelloComponent(props) {
return <div>Hello {props.name}</div>;
}
编译后(Babel):
function HelloComponent(props) {
return React.createElement(
"div",
null,
"Hello ",
props.name
);
}
类组件的内部实现
// React 内部处理类组件的伪代码
function mountClassComponent(element) {
const { type, props } = element;
// 实例化组件
const instance = new type(props);
// 调用生命周期
if (instance.componentWillMount) {
instance.componentWillMount();
}
// 调用 render
const renderedElement = instance.render();
// 继续渲染子树
return mount(renderedElement);
}
最佳实践
优先使用函数组件
// 推荐:函数组件 + Hooks
import React, { useState, useCallback, useMemo } from 'react';
function UserList({ users }) {
const [filter, setFilter] = useState('');
const filteredUsers = useMemo(() => {
return users.filter(user =>
user.name.toLowerCase().includes(filter.toLowerCase())
);
}, [users, filter]);
const handleFilterChange = useCallback((e) => {
setFilter(e.target.value);
}, []);
return (
<div>
<input
value={filter}
onChange={handleFilterChange}
placeholder="Search users..."
/>
<ul>
{filteredUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
何时使用类组件
// 错误边界必须使用类组件
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// 错误上报
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
组件拆分原则
// 不推荐:大而全的组件
function Dashboard() {
return (
<div>
<header>...</header>
<sidebar>...</sidebar>
<main>
<chart>...</chart>
<table>...</table>
</main>
<footer>...</footer>
</div>
);
}
// 推荐:拆分成小组件
function Dashboard() {
return (
<div>
<Header />
<Sidebar />
<MainContent />
<Footer />
</div>
);
}
function MainContent() {
return (
<main>
<SalesChart />
<UserTable />
</main>
);
}
组件命名规范
// 组件名使用 PascalCase
function UserProfile() { }
class ShoppingCart extends React.Component { }
// 文件名与组件名一致
// UserProfile.js
// ShoppingCart.js
// 私有组件以下划线开头
function _PrivateHelper() { }
Props 设计原则
// 好的 props 设计
function Button({
children,
onClick,
variant = 'primary', // 默认值
disabled = false,
size = 'medium'
}) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
}
// 使用
<Button variant="secondary" size="large" onClick={handleClick}>
Click Me
</Button>
面试要点
-
三种构建方式
- 函数组件:简单、现代、推荐
- React.createClass:已废弃
- 类组件:完整功能,但较复杂
-
函数组件的优势
- 代码简洁
- 没有 this 绑定问题
- 更容易测试
- 逻辑复用通过 Hooks
-
Hooks 的出现
- 让函数组件拥有状态
- 替代生命周期方法
- 更直观的逻辑复用
-
类组件的保留场景
- 错误边界
- 遗留代码维护
- 某些特定生命周期需求
-
常见面试题
- React 创建组件的方式有哪些?
- 函数组件和类组件的区别?
- 为什么 React.createClass 被废弃?
- Hooks 能否完全替代类组件?
-
现代 React 趋势
- 函数组件是主流
- 类组件不再新增特性
- 服务端组件(Server Components)基于函数组件