图片由 Ryan Quintal 拍摄,来自 Unsplash
记得你小时候打开一个新的乐高套装时的感觉吗?那种兴奋感,清晰的说明书,拼装零件时那种完美的契合所带来的满足感。现在回想一下你最近审查的那个所谓的“灵活”的企业组件的代码。感觉是不是完全不同了?
经过多年观察,看着开发者(包括我自己)在过度设计的方案中挣扎,我认识到最优秀的React架构与乐高积木有更多的共同点,而不是我们有时候尝试构建的那种复杂机械。
乐高原理:简单模块,无限可能的组合 我五岁的女儿让我学到的关于API设计的一些想法上周末,我看我女儿玩她的乐高玩具。“今天我们来建一个狗狗公园!”她兴奋地说。没有说明书或“资深乐高建筑师”的指导,她开始建造滑梯、沙坑、水池、喂食站和狗狗活动区。第二天?“我们来造车吧!”就这样,整个狗狗公园就变成了许多辆车。
这给我很大的触动。我的5岁孩子直观地理解了我们在软件开发中常会复杂化的问题:有了正确的积木,你可以创造任何东西,改变一切,而无需担心,可以放心地重建。每块乐高积木都有着明确的用途,以显而易见的方式组合,并且每次都能可靠地发挥作用。
与此同时,回到工作中,我们有一个所谓的“灵活”组件,看起来像这样:
// “企业级且灵活”的方法
const EnterpriseDataComponent = ({
data,
configurationMatrix,
transformationPipeline,
errorBoundaryStrategy,
optimizationHeuristics,
// … 20多个令人望而却步的属性
}) => {
useEffect(() => {
// 初始化逻辑
}, [/* 依赖数组比我点的咖啡还长 */]);
return (
// 会令资深开发者心酸的 JSX
);
};
我们的方法非常出了大问题。
乐高大师级玩家的React指南这张照片是由 Glen Carrie 在 Unsplash 拍摄的。
1. 从砖块着手 // 一个基本的乐高积木块 - 它完美地完成一件事情
const Button = ({ onClick, children }) => (
<button
onClick={onClick}
className={styles.button}
>
{children}
</button>
);
// 一个稍微更专业的乐高积木块
const ConfirmButton = (props) => (
<Button
{...props}
className={styles.confirm}
/>
);
2. 用简单的零件搭建更大的东西
// 创建一个有用的确认对话框
const ConfirmationDialog = () => {
const { confirm, cancel } = useDialog();
return (
<Dialog>
<DialogTitle>确定吗?</DialogTitle>
<DialogActions>
<ConfirmButton onClick={confirm}>好的</ConfirmButton>
<Button onClick={cancel}>取消</Button>
</DialogActions>
</Dialog>
);
};
3. 简化繁琐的机械
// 虽然有复杂的机制,但都被隐藏起来了
const useDialog = (options = {}) => {
// 第1级:简单路径(90%的开发人员只需要这一部分)
const simplePath = {
confirm: () => handleConfirm(),
cancel: () => handleCancel()
};
// 第2级:高级功能(9%的情况)
if (options.advanced) {
return {
...simplePath,
onBeforeConfirm: handlePreConfirm,
onAfterCancel: handlePostCancel
};
}
// 第3级:紧急出口(1%的高阶用户)
if (options._superPowers) {
return {
...simplePath,
_internal: internalAPIs,
_context: dialogContext
};
}
return simplePath;
};
你的全能设计工具箱
不要把你的设计系统仅仅看作一组组件,而是把它看作一个乐高大师级拼砌工具包。每个组件都应该像乐高积木一样。
这张照片由 Eric & Niklas 在 Unsplash 分享。
一目了然const Card = ({ title, children }) => (
<div className={styles.card}>
{title && <h2>{title}</h2>}
{children}
</div>
);
能够预见的连接性
// 自然配合工作的组件。
const ProductCard = ({ product }) => (
<Card>
<CardImage src={product.image} />
<CardContent>
<ProductDetails product={product} />
</CardContent>
<CardActions>
<AddToCartButton product={product} />
</CardActions>
</Card>
);
安全扩展性
// 高级开发人员可以在不破坏简洁性的情况下添加功能,依然保持简单。
const withAnalytics = (WrappedComponent) => {
return function EnhancedComponent(props) {
useAnalytics(props.analyticsEvent);
return <WrappedComponent {...props} />;
};
};
// 使用起来仍然很简单
const TrackableButton = withAnalytics(Button);
安全优势:更少的入口,更好的控制
照片由Yulia Matvienko拍摄/分享。
当你用定义明确的“乐高积木”构建时,安全就会变得更容易管理
// 一个安全的实现,适用于所有场景
const SecureButton = ({ onClick, children }) => {
const { validatePermissions } = useAuth();
const handleClick = () => {
if (validatePermissions()) {
onClick();
}
};
return <Button onClick={handleClick}>{children}</Button>;
};
// 开发者不会不小心跳过安全措施
const DeleteButton = () => (
<SecureButton
permission="权限:删除项目"
onClick={handleDelete}
>
删除
</SecureButton>
);
不想不到的好处
在实施这些原则之后,我们发现了一些意想不到的好处和意外收获。
初级开发者更快地上手了— ‘这确实合情合理’ 是一个常见的反馈。因此,少读文档,多动手。
资深开发人员发现优化起来更容易— 清晰的界限 使性能改进更加独立
— 更容易实现更复杂的功能而不会影响基本使用
— 不再是 “为什么这么复杂?” ,
— 而是变成了 “这正是我们要的”
— 当你的API简单易懂时,就不需要太多文档。因此,所写的文档会更加专注于高级应用场景。
建筑大师的智慧?记住:
- 如果你的组件需要博士才能理解,你可能做错了
- 复杂的功能应该是可选择加入的,而不是默认就有的
- 最好的代码不需要解释
- 为大多数人构建,为少数人扩展功能
感谢Ravi Palwe拍摄,来自Unsplash
React开发的未来不在于谁能够构建最复杂的系统,而在于谁能够使复杂系统感觉起来简单。就像乐高积木那样,已经做了好几代。
下次当你设计一个组件或系统时,问自己:“一个5岁的孩子能否理解这些零件是如何组合在一起的?”(好吧,也许代码本身可能有点复杂,但 心智模型也应该像这样清晰易懂。)
——
你是怎么让你的React组件既简单又强大的?在下面的评论中分享你的乐高技巧!