前端组件库实战:告别重复造轮子
为什么要引入组件库
在前端工程化实践中,重复编写基础 UI 组件是常见的痛点。想象一下,每个按钮、输入框都要单独定义样式,不仅耗时,还极易造成视觉风格割裂。与其从零开始'造轮子',不如站在巨人的肩膀上。
常见误区:手写组件的代价
很多新手习惯直接写原生 HTML 配合内联样式,虽然灵活,但扩展性极差。
// Button.jsx
import React from 'react';
function Button({ children, onClick }) {
return (
<button
onClick={onClick}
style={{ padding: '10px 20px', backgroundColor: '#007bff', color: '#fff' }}
>
{children}
</button>
);
}
export default Button;
// Input.jsx
import React from 'react';
function Input({ value, onChange, placeholder }) {
return (
<input
type="text"
value={value}
onChange={onChange}
placeholder={placeholder}
style={{ padding: '10px', border: '1px solid #ddd' }}
/>
);
}
export default Input;
这种写法的问题在于,一旦需求变更,比如主题色调整,你需要遍历所有文件修改硬编码的样式。更糟糕的是,不同开发者手写的组件很难保持统一的交互体验。
解决方案:成熟的组件库生态
1. Ant Design
企业级后台系统的首选,API 设计成熟,文档完善。
// App.jsx
import React from 'react';
import { Button, Input, Card, Form, Table, Modal, notification } from 'antd';
import 'antd/dist/reset.css';
function App() {
const [isModalVisible, setIsModalVisible] = React.useState(false);
const showModal = () => setIsModalVisible(true);
const handleOk = () => {
setIsModalVisible(false);
notification.success({ message: '成功', description: '操作成功' });
};
const handleCancel = () => setIsModalVisible(false);
const columns = [
{ title: '姓名', dataIndex: 'name', key: 'name' },
{ title: '年龄', dataIndex: 'age', key: 'age' },
{ title: '操作', render: () => <Button type="primary">编辑</Button> },
];
const data = [
{ key: '1', name: '张三', age: 32 },
{ key: '2', name: '李四', age: 42 },
];
return (
<div style={{ padding: '20px' }}>
<Card title="用户管理">
<Button type="primary" onClick={showModal} style={{ marginBottom: '20px' }}>
添加用户
</Button>
<Table columns={columns} dataSource={data} />
</Card>
<Modal title="添加用户" open={isModalVisible} onOk={handleOk} onCancel={handleCancel}>
<Form>
<Form.Item label="姓名" name="name" rules={[{ required: true }]}>
<Input placeholder="请输入姓名" />
</Form.Item>
<Form.Item label="年龄" name="age" rules={[{ required: true }]}>
<Input type="number" placeholder="请输入年龄" />
</Form.Item>
</Form>
</Modal>
</div>
);
}
export default App;
2. Material UI
遵循 Google Material Design 规范,适合追求现代感的项目。
// App.jsx
import React from 'react';
import { Button, TextField, Card, CardContent, Table, Paper, Modal, Box } from '@mui/material';
function App() {
const [isModalVisible, setIsModalVisible] = React.useState(false);
const [name, setName] = React.useState('');
const [age, setAge] = React.useState('');
const handleClose = () => setIsModalVisible(false);
const handleSubmit = () => {
console.log('提交:', { name, age });
handleClose();
};
const rows = [
{ id: 1, name: '张三', age: 32 },
{ id: 2, name: '李四', age: 42 },
];
return (
<div style={{ padding: '20px' }}>
<Card sx={{ mb: 2 }}>
<CardContent>
<Button variant="contained" color="primary" onClick={() => setIsModalVisible(true)}>
添加用户
</Button>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>姓名</TableCell>
<TableCell>年龄</TableCell>
<TableCell>操作</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.id}>
<TableCell>{row.name}</TableCell>
<TableCell>{row.age}</TableCell>
<TableCell>
<Button variant="outlined" color="primary">编辑</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</CardContent>
</Card>
<Modal open={isModalVisible} onClose={handleClose}>
<Box sx={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', width: 400, bgcolor: 'background.paper', p: 4 }}>
<h2>添加用户</h2>
<TextField label="姓名" fullWidth value={name} onChange={(e) => setName(e.target.value)} />
<TextField label="年龄" type="number" fullWidth value={age} onChange={(e) => setAge(e.target.value)} />
<Box sx={{ mt: 2, display: 'flex', justifyContent: 'flex-end', gap: 1 }}>
<Button onClick={handleClose}>取消</Button>
<Button variant="contained" color="primary" onClick={handleSubmit}>提交</Button>
</Box>
</Box>
</Modal>
</div>
);
}
export default App;
3. Tailwind CSS + Shadcn UI
适合喜欢原子化 CSS 和高度定制化的团队,组件即代码。
// App.jsx
import React from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Table, TableBody, TableCell, TableHead, TableRow } from '@/components/ui/table';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Label } from '@/components/ui/label';
function App() {
const rows = [
{ id: 1, name: '张三', age: 32 },
{ id: 2, name: '李四', age: 42 },
];
return (
<div className="p-4">
<Card className="mb-4">
<CardHeader>
<CardTitle>用户管理</CardTitle>
</CardHeader>
<CardContent>
<Dialog>
<DialogTrigger asChild>
<Button className="mb-4">添加用户</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>添加用户</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="name">姓名</Label>
<Input placeholder="请输入姓名" />
</div>
<div className="grid gap-2">
<Label htmlFor="age">年龄</Label>
<Input type="number" placeholder="请输入年龄" />
</div>
</div>
<div className="flex justify-end gap-2">
<Button variant="secondary">取消</Button>
<Button>提交</Button>
</div>
</DialogContent>
</Dialog>
<div className="overflow-auto">
<Table>
<TableHead>
<TableRow>
<TableCell>姓名</TableCell>
<TableCell>年龄</TableCell>
<TableCell>操作</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.id}>
<TableCell>{row.name}</TableCell>
<TableCell>{row.age}</TableCell>
<TableCell>
<Button variant="secondary">编辑</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
</div>
);
}
export default App;
选择合适组件库的关键在于团队技术栈与项目需求。Ant Design 胜在开箱即用,Material UI 强在设计规范,而 Shadcn UI 则提供了更高的灵活性。无论选哪个,核心目标都是让开发者专注于业务逻辑,而非纠结于像素级的对齐。

