"这个表单样式改不动啊!"周二下午,小王抓着头发对我说。我走过去一看,原来他正在尝试修改 Ant Design 的 Form 组件样式,想让它符合新的设计规范。这已经是本周第三次遇到类似的问题了。
作为一个运行了两年的后台管理系统,我们一直在使用 Ant Design。但随着产品的不断发展,定制化需求越来越多,覆盖第三方组件库的样式变得越来越困难。一次技术分享会上,我了解到了 shadcn/ui 这个解决方案,它的理念让我眼前一亮。经过团队讨论,我们决定尝试将部分模块从 Ant Design 迁移到 shadcn/ui。
迁移准备
首先,我们需要评估迁移的成本和风险。我列了一个清单:
- 需要迁移的组件数量:约 30 个
- 受影响的业务模块:用户管理、订单管理
- 预计工作量:2-3 周
- 潜在风险:样式不一致、功能缺失
就像装修房子要先搭脚手架,我们也需要先做好准备工作:
// 创建迁移配置文件
// migration.config.ts
interface MigrationConfig {
// 需要迁移的路由
routes: string[]
// 组件映射关系
componentMap: Record<string, string>
// 特性对比
featureMap: Record<
string,
{
antd: string[]
shadcn: string[]
missing: string[]
}
>
}
const migrationConfig: MigrationConfig = {
routes: ['/users', '/orders'],
componentMap: {
'antd/lib/button': '@/components/ui/button',
'antd/lib/input': '@/components/ui/input',
'antd/lib/select': '@/components/ui/select'
},
featureMap: {
Button: {
antd: ['loading', 'ghost', 'danger'],
shadcn: ['loading', 'variant', 'size'],
missing: ['ghost']
}
}
}
渐进式迁移
为了降低风险,我们采用了渐进式迁移策略。首先从最简单的按钮组件开始:
// 迁移前的 Ant Design 按钮
import { Button } from 'antd'
function UserActions({ user }) {
return (
<div className='actions'>
<Button type='primary' onClick={() => handleEdit(user)}>
编辑
</Button>
<Button danger onClick={() => handleDelete(user)}>
删除
</Button>
</div>
)
}
// 迁移后的 shadcn/ui 按钮
import { Button } from '@/components/ui/button'
function UserActions({ user }) {
return (
<div className='flex gap-2'>
<Button variant='default' onClick={() => handleEdit(user)}>
编辑
</Button>
<Button variant='destructive' onClick={() => handleDelete(user)}>
删除
</Button>
</div>
)
}
然后是表单组件,这是最具挑战性的部分:
// 迁移前的 Ant Design 表单
import { Form, Input, Select } from 'antd'
function UserForm({ initialValues, onSubmit }) {
const [form] = Form.useForm()
return (
<Form form={form} initialValues={initialValues} onFinish={onSubmit} labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
<Form.Item label='用户名' name='username' rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item label='角色' name='role'>
<Select>
<Select.Option value='admin'>管理员</Select.Option>
<Select.Option value='user'>普通用户</Select.Option>
</Select>
</Form.Item>
</Form>
)
}
// 迁移后的 shadcn/ui 表单
import { useForm } from 'react-hook-form'
import { Form, FormField, FormItem, FormLabel, FormControl } from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select'
function UserForm({ defaultValues, onSubmit }) {
const form = useForm({
defaultValues,
resolver: zodResolver(userSchema)
})
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className='space-y-4'>
<FormField
control={form.control}
name='username'
render={({ field }) => (
<FormItem>
<FormLabel>用户名</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name='role'
render={({ field }) => (
<FormItem>
<FormLabel>角色</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<SelectTrigger>
<SelectValue placeholder='选择角色' />
</SelectTrigger>
<SelectContent>
<SelectItem value='admin'>管理员</SelectItem>
<SelectItem value='user'>普通用户</SelectItem>
</SelectContent>
</Select>
</FormItem>
)}
/>
</form>
</Form>
)
}
样式迁移
样式迁移是个大工程,我们采用了"主题变量映射"的方式:
// styles/theme-mapping.ts
const antdToShadcnMapping = {
// 颜色映射
'@primary-color': 'hsl(var(--primary))',
'@success-color': 'hsl(var(--success))',
'@warning-color': 'hsl(var(--warning))',
'@error-color': 'hsl(var(--destructive))',
// 字体映射
'@font-size-base': '14px',
'@font-size-lg': '16px',
'@font-size-sm': '12px',
// 圆角映射
'@border-radius-base': 'var(--radius)',
'@border-radius-sm': 'calc(var(--radius) - 2px)'
}
// 生成 CSS 变量
function generateCSSVariables() {
return Object.entries(antdToShadcnMapping)
.map(([antd, shadcn]) => {
const name = antd.replace('@', '--')
return `${name}: ${shadcn};`
})
.join('\n')
}
遇到的挑战
迁移过程中我们遇到了不少挑战:
- Form 组件的心智模型完全不同
- 一些 Ant Design 的特性在 shadcn/ui 中没有对应实现
- 团队需要适应新的开发方式
但这些挑战也带来了意外收获:
- 代码更容易维护了
- 打包体积减小了
- 定制化变得更简单了
- 团队对组件原理理解更深了
效果验证
迁移完成后,我们对比了一些关键指标:
- 打包体积:从 2.8MB 减少到 1.2MB
- 首屏加载时间:从 2.1s 减少到 1.3s
- 样式覆盖代码:减少了 80%
- 开发效率:提升了约 30%
最让我印象深刻的是小王的反馈:"现在改样式太舒服了,再也不用和 Ant Design 的样式战斗了!"
经验总结
这次迁移让我们学到了很多:
- 渐进式迁移比全量替换更可行
- 组件库的选择要考虑长期维护成本
- 看似简单的改变可能带来意想不到的好处
- 团队的技术成长比迁移本身更重要
就像装修房子,有时候推倒重来比反复修补更划算。但推倒重来的过程要有计划,分步进行。
写在最后
组件库迁移不仅仅是技术栈的更新,更是一次团队能力的提升。正如那句话说的:"工具是死的,人是活的。"选择合适的工具很重要,但更重要的是团队能够驾驭这些工具。
有什么问题欢迎在评论区讨论,让我们一起探讨组件库迁移的经验!
如果觉得有帮助,别忘了点赞关注,我会继续分享更多实战经验~