跳到主要内容前端组件库:拒绝重复造轮子 | 极客日志JavaScript大前端
前端组件库:拒绝重复造轮子
探讨了前端开发中使用组件库的必要性,指出手动编写组件会导致样式不统一和维护困难。通过对比手动实现与使用主流组件库(如 Ant Design、Material UI、Tailwind CSS + Shadcn UI)的代码示例,展示了利用成熟组件库如何提升开发效率和代码质量,建议开发者避免重复造轮子。
虚拟内存0 浏览 前端组件库:拒绝重复造轮子
引言
这组件写得跟拼凑似的,一点都不统一。
前端开发中,手动编写所有组件如同无工具建房,效率低下。
为什么你需要组件库
许多项目手动编写组件导致样式不统一、维护困难,这本质上是重复劳动。
反面教材
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;
import React from 'react';
function Input({ value, onChange, placeholder }) {
return (
<input type= = = = = '', ' #', '', '%' }} />
);
}
;
;
() {
(
);
}
;
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
"text"
value
{value}
onChange
{onChange}
placeholder
{placeholder}
style
{{
padding:
10px
border:
1px
solid
ddd
borderRadius:
4px
width:
100
export
default
Input
import
React
from
'react'
function
Card
{ children }
return
<div style={{ padding: '20px', border: '1px solid #ddd', borderRadius: '4px', boxShadow: '0 2px 4px rgba(0,0,0,0.1)' }}>
{children}
</div>
export
default
Card
总结:这种编写方式就像在重复造轮子,每个组件都要手动编写样式,效率低得可怜。
正确姿势
1. Ant Design
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 = = = , '' }]}>
);
}
export default App;
2. Material UI
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}>
{row.name}
{row.age}
编辑
))}
添加用户
setName(e.target.value)} />
setAge(e.target.value)} />
取消
提交
);
}
export default App;
3. Tailwind CSS + Shadcn UI
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
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= />
年龄
取消
提交
姓名
年龄
操作
{rows.map((row) => (
{row.name}
{row.age}
编辑
))}
);
}
export default App;
总结:这才叫前端组件库,统一的样式,丰富的组件,开发效率高,再也不用担心重复造轮子的问题了。
label
"年龄"
name
"age"
rules
{[{
required:
true
message:
请输入年龄
<Input type="number" placeholder="请输入年龄" />
</Form.Item>
</Form>
</Modal>
</div>
<TableCell>
</TableCell>
<TableCell>
</TableCell>
<TableCell>
<Button variant="outlined" color="primary">
</Button>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</CardContent>
</Card>
<Modal open={isModalVisible} onClose={handleClose} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description">
<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) =>
<TextField label="年龄" type="number" fullWidth value={age} onChange={(e) =>
<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>
"请输入姓名"
</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>
<TableRow key={row.id}>
<TableCell>
</TableCell>
<TableCell>
</TableCell>
<TableCell>
<Button variant="secondary">
</Button>
</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
</CardContent>
</Card>
</div>