前端组件库:拒绝重复造轮子,提升开发效率
行业痛点
近期观察到部分项目存在手动编写所有组件的情况,导致样式不统一、维护困难。这种模式类似于在没有工具的情况下盖房子——虽然可行,但效率极低。
反面案例
// 反面案例:手动编写组件
import React from 'react';
function Button({ children, onClick }) {
return (
<button
onClick={onClick}
style={{ padding: '10px 20px', backgroundColor: '#007bff', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer' }}
>
{children}
</button>
);
}
export default Button;
上述代码中,每个组件都需要手动编写样式,不仅代码冗余,且难以保证视觉一致性。
推荐方案
1. Ant Design
// 安装依赖
// npm install antd
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: '操作', key: 'action', 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, message: '请输入姓名' }]}>
<Input placeholder="请输入姓名" />
</Form.Item>
<Form.Item label="年龄" name="age" rules={[{ required: true, message: '请输入年龄' }]}>
<Input type="number" placeholder="请输入年龄" />
</Form.Item>
</Form>
</Modal>
</div>
);
}
export default App;
2. Material UI
// 安装依赖
// npm install @mui/material @emotion/react @emotion/styled
import React from 'react';
import { Button, TextField, Card, CardContent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, 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 showModal = () => setIsModalVisible(true);
const handleClose = () => setIsModalVisible(false);
const handleSubmit = () => { console.log('提交:', { name, age }); setIsModalVisible(false); };
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={showModal} sx={{ mb: 2 }}>添加用户</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', border: '2px solid #000', boxShadow: 24, 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
// 安装依赖
// npm install -D tailwindcss postcss autoprefixer
// npx tailwindcss init -p
// npx shadcn@latest init
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;
使用成熟的组件库能确保统一的样式规范,提供丰富的功能组件,显著提升开发效率,避免重复造轮子的问题。

