组件化思想
下面,我们来系统的梳理关于 React 组件化思想 的基本知识点:
一、组件化基础概念
1.1 什么是组件化?
组件化是将UI拆分为独立、可复用代码单元的开发范式。在React中,组件是构建用户界面的基本单位,每个组件封装了:
- 结构(JSX)
- 样式(CSS)
- 行为(JavaScript)
- 状态(State)
- 交互逻辑
1.2 为什么需要组件化?
优势 | 说明 |
---|---|
复用性 | 一次开发,多处使用 |
可维护性 | 独立更新不影响其他部分 |
分而治之 | 复杂问题拆解为小问题 |
团队协作 | 并行开发不同组件 |
可测试性 | 独立单元易于测试 |
1.3 React组件类型
类型 | 特点 | 适用场景 |
---|---|---|
函数组件 | 无状态、简洁 | 展示型组件 |
类组件 | 有生命周期、状态 | 复杂交互组件(React 16.8前主流) |
Hooks组件 | 函数组件+状态能力 | 现代React开发首选 |
二、组件设计原则
2.1 单一职责原则
一个组件只做一件事:
// 反例:混合了展示和逻辑
function UserListWithActions() {// ... 数据获取、渲染列表、操作处理
}// 正例:拆分
function UserList({ users }) {// 只负责渲染
}function UserContainer() {const [users, setUsers] = useState([]);// 负责数据获取return <UserList users={users} />
}
2.2 封装与隔离
组件应隐藏内部实现细节,只暴露必要接口:
// 计数器组件
function Counter() {const [count, setCount] = useState(0);// 内部实现细节,外部无需知道const increment = () => setCount(c => c + 1);return (<div><button onClick={increment}>+</button><span>{count}</span></div>);
}
2.3 组合优于继承
React推荐使用组合而非继承构建复杂UI:
// 使用组合的弹窗组件
function Dialog({ title, children }) {return (<div className="dialog"><h2>{title}</h2><div className="content">{children} {/* 内容由外部传入 */}</div></div>);
}// 使用
<Dialog title="警告"><p>确定要删除吗?</p><div className="actions"><button>取消</button><button>确认</button></div>
</Dialog>
三、组件通信模式
3.1 父传子:Props
function Parent() {const data = "Hello from parent";return <Child message={data} />;
}function Child({ message }) {return <p>{message}</p>;
}
3.2 子传父:回调函数
function Parent() {const handleChildEvent = (data) => {console.log("来自子组件的数据:", data);};return <Child onEvent={handleChildEvent} />;
}function Child({ onEvent }) {return (<button onClick={() => onEvent(Math.random())}>发送数据</button>);
}
3.3 兄弟组件通信
通过状态提升到共同父组件:
function Parent() {const [sharedState, setSharedState] = useState(null);return (<><SiblingA state={sharedState} setState={setSharedState} /><SiblingB state={sharedState} setState={setSharedState} /></>);
}
3.4 深层嵌套组件通信
使用Context API:
const ThemeContext = createContext('light');function App() {return (<ThemeContext.Provider value="dark"><DeepNestedComponent /></ThemeContext.Provider>);
}function DeepNestedComponent() {const theme = useContext(ThemeContext);return <div>当前主题: {theme}</div>;
}
四、组件生命周期(类组件)
4.1 生命周期图谱
4.2 关键生命周期方法
- constructor:初始化state,绑定方法
- componentDidMount:DOM操作、数据请求
- shouldComponentUpdate:性能优化关键
- componentDidUpdate:更新后DOM操作
- componentWillUnmount:清理定时器/订阅
五、函数组件与Hooks
5.1 函数组件优势
- 代码更简洁
- 无this绑定问题
- 易于测试
- 更好的类型推断
5.2 核心Hooks
Hook | 作用 | 等效生命周期 |
---|---|---|
useState | 状态管理 | this.setState |
useEffect | 副作用处理 | componentDidMount + componentDidUpdate + componentWillUnmount |
useContext | 访问Context | static contextType |
useRef | 引用DOM/保存可变值 | createRef |
useMemo | 记忆计算结果 | shouldComponentUpdate |
useCallback | 记忆函数 | 方法绑定优化 |
5.3 自定义Hook
封装可复用逻辑:
function useWindowSize() {const [size, setSize] = useState({width: window.innerWidth,height: window.innerHeight});useEffect(() => {const handleResize = () => setSize({width: window.innerWidth,height: window.innerHeight});window.addEventListener('resize', handleResize);return () => window.removeEventListener('resize', handleResize);}, []);return size;
}// 使用
function MyComponent() {const { width } = useWindowSize();return <p>窗口宽度: {width}px</p>;
}
六、组件设计模式
6.1 容器组件与展示组件
容器组件 | 展示组件 | |
---|---|---|
职责 | 数据获取、业务逻辑 | UI呈现 |
复用性 | 低 | 高 |
状态 | 通常有状态 | 通常无状态 |
示例 | 用户数据容器 | 用户卡片 |
6.2 高阶组件(HOC)
接收组件,返回增强组件:
function withLogger(WrappedComponent) {return function(props) {useEffect(() => {console.log(`组件 ${WrappedComponent.name} 已渲染`);}, []);return <WrappedComponent {...props} />;};
}const EnhancedComponent = withLogger(MyComponent);
6.3 渲染属性(Render Props)
通过函数prop共享代码:
class MouseTracker extends React.Component {state = { x: 0, y: 0 };handleMouseMove = (e) => {this.setState({ x: e.clientX, y: e.clientY });};render() {return (<div onMouseMove={this.handleMouseMove}>{this.props.render(this.state)}</div>);}
}// 使用
<MouseTracker render={({ x, y }) => (<p>鼠标位置: {x}, {y}</p>
)} />
七、组件性能优化
7.1 避免不必要的渲染
- React.memo:记忆函数组件
const MemoComponent = React.memo(MyComponent);
- PureComponent:类组件浅比较
class PureComp extends React.PureComponent {}
7.2 正确使用key
// 列表项使用稳定唯一key
{items.map(item => (<ListItem key={item.id} item={item} />
))}
7.3 懒加载组件
const LazyComponent = React.lazy(() => import('./HeavyComponent'));function App() {return (<Suspense fallback={<Spinner />}><LazyComponent /></Suspense>);
}
八、组件测试策略
8.1 测试金字塔
8.2 测试工具
- Jest:测试框架
- React Testing Library:组件测试
- Cypress:端到端测试
8.3 组件测试示例
import { render, screen, fireEvent } from '@testing-library/react';test('按钮点击后显示文本', () => {render(<Button />);fireEvent.click(screen.getByText('点击'));expect(screen.getByText('已点击')).toBeInTheDocument();
});
九、组件化最佳实践
- 命名规范:组件使用PascalCase,props使用camelCase
- 文件结构:一个组件一个文件夹,包含组件、样式、测试
components/Button/Button.jsxButton.module.cssButton.test.jsindex.js
- PropTypes:定义props类型(TypeScript更优)
Button.propTypes = {variant: PropTypes.oneOf(['primary', 'secondary']),onClick: PropTypes.func.isRequired
};
- 文档驱动开发:使用Storybook组件目录
十、组件设计实战
10.1 设计可配置按钮
function Button({children,variant = 'primary',size = 'medium',disabled = false,onClick
}) {const classNames = `btn btn-${variant} btn-${size} ${disabled ? 'disabled' : ''}`;return (<button className={classNames} disabled={disabled}onClick={onClick}>{children}</button>);
}
10.2 构建表单组件体系
function Form({ children, onSubmit }) {return <form onSubmit={onSubmit}>{children}</form>;
}function FormItem({ label, children, error }) {return (<div className="form-item"><label>{label}</label>{children}{error && <div className="error">{error}</div>}</div>);
}function Input({ ...props }) {return <input className="form-input" {...props} />;
}// 使用
<Form onSubmit={handleSubmit}><FormItem label="用户名" error={errors.username}><Input value={username} onChange={e => setUsername(e.target.value)}/></FormItem>
</Form>
总结
- 组件是React应用的构建块,遵循单一职责原则
- 通过Props、Context、自定义事件实现组件通信
- 函数组件+Hooks是现代化React开发首选
- 使用组合模式构建复杂UI
- 通过React.memo、正确使用key等优化性能
- 建立组件文档和测试保障质量