组件化思维是一种将应用程序的 UI 划分为独立、可复用的模块(即组件)的开发理念。在现代前端开发中,尤其是在框架如 React 中,组件化是核心思想之一。
一个中级开发者,其核心特质之一便应体现在具备卓越的组件化思维。这种思维模式要求开发者能够将复杂的系统分解为若干个功能明确、易于管理和复用的组件,从而提升开发效率与项目质量。
案例
接下来看一个例子:
/**
* @license MIT
* Created by: joetao
* Created on: 2025/1/6
* Project: web
* Description: 用户卡片
*/
import {ReactNode} from "react";
interface UserCardProps {
data: any
type: 'employee' | 'customer'
showHeader?: boolean,
layout?: 'detailed' | 'compact',
className?: string
}
const UserCard = ({
data,
type,
layout = 'detailed',
showHeader = true,
className = '',
}: UserCardProps) => {
const renderHeader = (): ReactNode => {
if (!showHeader) return null;
return (
<div>
<h2>{data.name}</h2>
{type === 'employee' ? (
<>
<p>雇员角色: {data.role}</p>
</>
) : (
<p>消费者等级: {data.tier}</p>
)}
</div>
)
}
const renderDetail = (): ReactNode => {
return (
<div className={'p-4'}>
{type === 'employee' ? (
<>
<p>所属部门: {data.department}</p>
<p>技能:</p>
{data.skills.map((skill: string) => (
<span>{skill}</span>
))}
</>
) : (
<p>最后购物时间: {data.lastPurchaseDate}</p>
)
}
</div>
)
}
return (
<div className={`rounded-lg border shadow ${className}`}>
{renderHeader()}
{layout === 'detailed' && renderDetail()}
</div>
)
}
export default UserCard
这个用户卡片可以根据传入的type参数不同用于渲染消费者和雇员的信息。
function App() {
const employeeData = {
id: 'E123',
name: 'joetao',
role: "工程师",
department: 'Engineering',
skills: ['react', 'vue', 'java']
}
const customerData = {
id: 'C123',
name: "Json",
tier: "VIP",
lastPurchaseDate: '2025-01-06'
}
return (
<>
<UserCard
data={employeeData}
type={'employee'}
showHeader={true}
layout={'detailed'}
className={'bg-blue-500'}
/>
<UserCard data={customerData} type={'customer'} />
</>
)
}
export default App
UserCard组件主要的一个问题是:有一些逻辑需要通过判断传入的type类型来决定渲染内容,这会导致组件设计的比较复杂,会有很多条件判断;而且如果出现新的人员类型,需要修改UserCard,增加新的条件判断,这样就非常不优雅了。这种情况在软件开发中非常常见,这违背了组件职责单一、可拓展性的问题。
重构
那么该如何重构它呢?
我们应该抽离出EmployeeCard和CustomerCard组件,每个组件只渲染一类人员。使用这种方式,可以让组件职责单一,同时当有新的人员类型时,不需要修改原有的组件,定义一个新的组件就可以。然后使用组合的方式,将它们组合到一起。
重构之后的样子:
我们首先定义了一个通用的组件BaseCard:
import {Card} from "@/components/ui/card";
import {ReactNode} from "react";
interface BaseCardProps {
children: ReactNode
className?: string
}
export const BaseCard = ({ children, className = "" }: BaseCardProps) => {
return (
<Card className={`rounded-lg border shadow`}>{children}</Card>
)
}
然后抽离出雇员卡片组件和消费者卡片组件:
import {BaseCard} from "@/components/BaseCard";
interface EmployeeCardProps {
id: string,
name: string,
role: string,
department: string
skills: string[]
}
const Header = (name: string) => (
<h2>姓名: {name}</h2>
)
const Detail = ({
role,
department,
skills}: {
role: string,
department: string
skills: string[]
}) => (
<div>
<p>雇员角色: {role}</p>
<p>所属部门: {department}</p>
<p>技能:</p>
{skills.map((skill: string) => (
<span>{skill}</span>
))}
</div>
)
const EmployeeCard = ({data, className = ''}: EmployeeCardProps) => {
return (
<BaseCard className={className}>
<Header name={data.name}/>
<Detail role={data.role} department={data.department} skills={data.skills} />
</BaseCard>
)
}
export default EmployeeCard
CustomerCard大家可以想一下如何实现。
在EmployeeCard中,我们抽离了一个Header和Detail组件,通过组合的方式实现EmployeeCard组件。
使用新的组件:
import UserCard from "@/components/UserCard";
import EmployeeCard from "@/components/EmployeeCard";
function App() {
const employeeData = {
id: 'E123',
name: 'joetao',
role: "工程师",
department: 'Engineering',
skills: ['react', 'vue', 'java']
}
const customerData = {
id: 'C123',
name: "Json",
tier: "VIP",
lastPurchaseDate: '2025-01-06'
}
return (
<>
<EmployeeCard data={employeeData} className={'bg-gray-500'}/>
<CustomerCard data={customerData} className={'bg-gray-500'}/>
</>
)
}
export default App
组件化思维是我们开发者必须具备的一个核心能力,如何将代码组件化不仅仅是实现功能的时候需要考虑,重构的过程中也是需要重点考虑的地方。
总结
- 组件职责做到单一化
- 多使用组合
- 提取公共通用的组件
重构前后的代码没有绝对的好与坏,全凭大家的经验和认知来选择是否认可。以上观点也仅仅给大家提供一个参考。
另外推荐一下React+Spring Boot的开源项目:github.com/pipijoe/xry…