5.1 大型应用架构设计


文档摘要

5.1 大型应用架构设计 本节摘要:大型 React 应用的架构不是「很多组件加起来」,而是「有组织地组合」。本节从原子设计的组件分层讲起,再谈 UI/逻辑/数据的分离,给出目录结构范例与组件设计三原则,最后覆盖状态规划、可访问性与安全基线——一套从「组件怎么写」到「项目怎么长」的架构方法。 上手前先明确 阅读完本节,你应当能够: 用原子设计思想划分组件层级 设计 UI 层、逻辑层、数据层分离的分层架构 遵循单一职责、关注点分离、避免过度抽象三原则 为大型应用规划目录结构与状态分配 落地可访问性与安全基线 一、问题与直觉:大型应用为什么需要架构 小型应用怎么乱都行——几十个组件放一个目录,state 随意放,也能跑。

5.1 大型应用架构设计

本节摘要:大型 React 应用的架构不是「很多组件加起来」,而是「有组织地组合」。本节从原子设计的组件分层讲起,再谈 UI/逻辑/数据的分离,给出目录结构范例与组件设计三原则,最后覆盖状态规划、可访问性与安全基线——一套从「组件怎么写」到「项目怎么长」的架构方法。

上手前先明确

阅读完本节,你应当能够:

  1. 用原子设计思想划分组件层级
  2. 设计 UI 层、逻辑层、数据层分离的分层架构
  3. 遵循单一职责、关注点分离、避免过度抽象三原则
  4. 为大型应用规划目录结构与状态分配
  5. 落地可访问性与安全基线

一、问题与直觉:大型应用为什么需要架构

小型应用怎么乱都行——几十个组件放一个目录,state 随意放,也能跑。但规模上去后,没有架构的项目会呈现典型症状:组件文件散落无从查找、一个 state 被二十个组件共享、改一个按钮的样式牵扯全局、新同事进来三个月找不到代码在哪。

架构的价值不是「好看」,是让代码的「位置」符合「职责」——看到路径知道里面是什么,看到组件知道它负责什么,看到 state 知道它属于谁。这样项目才能持续演进,而不是越改越乱直到重写。换句话说,架构的终极产物是「团队对代码的共同心智」,让每个决定都有据可循。

二、组件架构:原子设计的分层

原子设计把组件按「粒度」分成五层,从最基础到最完整:

  • Atoms 原子:最基本的 UI 元素,按钮、输入框、标签
  • Molecules 分子:原子组合成的小单元,带标签的输入框
  • Organisms 组织:分子+原子组成的页面区块,导航栏、表单
  • Templates 模板:定义页面整体结构,组合 Organisms
  • Pages 页面:模板 + 数据,最终呈现给用户
// 原子:Button function Button({ children, onClick, variant }) { return ( <button className={`btn btn-${variant}`} onClick={onClick}> {children} </button> ); } // 分子:带标签的输入框 function InputWithLabel({ label, ...props }) { return ( <div> <Label>{label}</Label> <Input {...props} /> </div> ); }

为什么这个分层有效

分层的核心价值是「依赖方向明确」:原子不依赖分子,分子不依赖组织,页面依赖模板。依赖只往下流,修改上层不影响下层——这是整个架构稳定性的基础。

原子设计分层架构

原子设计分层架构

这张图展示了五层的关系:自底向上从原子到页面,粒度越来越大;自顶向下从页面到原子,复用越来越多。中间层可以跳过——分子直接组成页面也行,分层是「组织思维」不是「强制规范」。

分层的实际收益:一次真实的组件变更

用一个具体场景体会分层的收益。假设产品要求把全站所有「主要按钮」的圆角从 4 改成 8:

没有分层时,按钮样式散落在各个页面组件里,每个页面改一遍,还要担心改漏。有原子层时,所有按钮都是 ,只改原子层 Button 一处,全站生效。这就是「修改上层不影响下层」的反面——修改基础层,所有上层一起受益。原子层是「一处修改、全局生效」的杠杆点,这是它存在的实际理由。

分层实战中的边界

原子层是「完全通用」的,不能掺业务逻辑——Button 不能知道「这是删除按钮所以要二次确认」。业务逻辑要放在 Organisms 或更上层。这条边界是分层架构最容易被打破的地方:业务逻辑下沉到基础组件,组件库就变成了「业务组件库」,无法复用、无法抽出。

判断「这个逻辑该不该进组件」的一个简单测试:把这个组件复制到一个全新项目,它还能原样工作吗? 能,说明它保持通用;不能,说明业务逻辑渗进去了。这个「可移植性测试」在组件设计时很管用。

三、分层架构:UI、逻辑、数据的分离

组件层管「粒度」,分层架构管「职责」。把应用分成三层:

  • UI 层:组件与样式,负责渲染与交互
  • 逻辑层:业务规则、状态管理、请求调用
  • 数据层:API 通信、数据模型、缓存

分离的价值在「每一层可以独立变化」。改接口格式只动数据层,改业务规则只动逻辑层,改样式只动 UI 层——互不牵连。

实际体现:自定义 Hook 是分层的关键

逻辑层在 React 里的落地形态是自定义 Hook。第 2.2 节讲过 useCounter、useWindowSize,这里的用法是「把组件的业务逻辑抽进 Hook,组件只剩渲染」:

// 逻辑层:数据获取 Hook function useUser(userId) { const [user, setUser] = useState(null); const [status, setStatus] = useState('idle'); useEffect(() => { setStatus('loading'); fetchUser(userId) .then(u => { setUser(u); setStatus('done'); }) .catch(() => setStatus('error')); }, [userId]); return { user, status }; } // UI 层:组件只管渲染 function UserProfile({ userId }) { const { user, status } = useUser(userId); if (status === 'loading') return <p>加载中...</p>; if (status === 'error') return <p>加载失败</p>; return <h1>{user.name}</h1>; }

「数据获取逻辑在 Hook 里、展示在组件里」就是三层分离的最小实践。逻辑可测、可复用,组件纯粹、可换皮——换一个数据源只改 Hook,换一套视觉只改组件,两层互不牵连。

四、组件设计三原则

单一职责原则

每个组件只负责一件事。第 1.4 节拆过 UserProfile 的例子——加载、错误、内容三态拆成三个组件。判断标准:这个组件有两个以上「变化的理由」吗?有,就拆。

关注点分离

逻辑与视图分离。第 1.4 节把计数器逻辑抽进 useCounter 就是例子。组件管「长什么样」,Hook 管「怎么算」,各自的修改互不影响。

避免过度抽象

不要为了抽象而抽象。第 3.5 节强调「模式是工具,先看问题再选」。架构同理——组件只有真到要复用时才抽出来,目录只有真到该分层时才分层。过度设计是「预测未来」,而未来你猜不准。判断抽象是否过度,问一句:这个抽象现在解决了具体问题,还是只解决了「未来可能有的问题」? 只解决未来的,就是过度。

// 违反 SRP:一个组件管加载+错误+展示 function UserProfile(props) { const { user, isLoading, error } = props; if (isLoading) return <div>Loading...</div>; if (error) return <div>Error</div>; return <div>{user.name}</div>; } // 符合 SRP:拆成三个独立组件

五、目录结构:让代码的位置符合职责

一套可参考的目录结构:

src/ components/ # 通用组件(原子/分子级) Button/ InputWithLabel/ features/ # 业务模块(Organisms/Templates/Pages) auth/ LoginPage.jsx useAuth.js products/ ProductList.jsx ProductDetail.jsx useProducts.js hooks/ # 跨模块的自定义 Hook api/ # 请求层,按模块组织 API 函数 utils/ # 通用工具 store/ # 全局状态 styles/ # 全局样式

关键设计:components 放通用组件,features 放业务模块。features 内部按业务域组织,每个域自包含(页面 + 专属 Hook + 专属组件)。这个「功能优先」的划分比「按类型堆文件」更适合中大型项目——找代码时先想「这是哪个业务的」,而不是「这是组件还是工具」。业务模块独立成域之后,模块边界(下文会讲)也有了天然的落点。

💡 关键直觉:目录结构是「团队导航系统」。好的结构让新成员「看路径猜内容」的准确率接近百分之百。判断目录好不好,问一句:别人(三个月后的你)要找「订单列表的删除逻辑」,能顺着路径两分钟内找到吗?

六、状态分配:全局状态越少越好

第 4.2 节的「数据分工」在这里落地成状态分配原则:

  • 组件状态:useState/useReducer,只属于当前组件
  • 共享状态:Context 或 Zustand,跨组件共享但低频
  • 服务器状态:数据请求库(TanStack Query),接口数据归它管

全局状态的三条红线:能放局部的别放全局,能派生的别存储,能归请求库的别进状态库。 全局状态越少,调试越容易,团队越少踩「谁改了我的全局状态」的坑。判断一条数据该放哪,就问「谁需要它、多久变一次」——用它的组件越少、变化越低频,放得越局部越好。

模块边界:大型应用的第二道墙

组件分层之外,大型应用还需要「模块边界」——不同业务模块之间怎么隔离。几个实用的边界约定:

模块间不直接访问对方的内部组件。 订单模块不能 import 用户模块的私有组件,跨模块的能力通过「共享层」(components、hooks、api 里的公共部分)提供。这条边界保证模块可以独立演进,改一个模块不牵连另一个。

模块内私有内容不导出。 模块里的中间组件、临时工具,用「仅在模块内使用」的约定收口,别把它们变成公共 API。公共接口越少,重构自由度越大。

跨模块状态通过明确通道。 模块间共享数据要么提升到共享层,要么走全局状态,别靠「import 对方的 store」这种隐式耦合。

模块边界的价值在团队变大时显现:几十个组件互相 import 的「一团乱麻」,和有明确边界的「若干区块」,维护成本是两个量级。架构设计到最后,设计的是「人与人之间的代码协作边界」。

七、可访问性与安全基线

架构设计还包括「底线」——从第一天就该在的规范。

可访问性基线:语义化 HTML(nav/main/article 标签)、图片 alt 文本、表单 label 关联、键盘可操作(Tab 导航全通)、颜色对比度达标。这些是「所有人都能用」的基础,不是加分项。特别是键盘可操作性——很多用户不靠鼠标,Tab 键走不通的应用对他们是「不可用的」。

安全基线:用户输入默认转义(React 默认做)、dangerouslySetInnerHTML 只用可信来源、敏感数据不进前端代码、环境变量区分公开与私密。

领域 基线要求
可访问性 语义标签、alt、label、键盘、对比度
安全 默认转义、可信 HTML、密钥不泄露
性能 首屏体积、按需加载、图片优化

架构是演进的,不是一次到位的

最后必须说一句反「架构崇拜」的话:架构不是项目第一天就设计完美的,它是随规模演进出来的。小项目上原子设计是负担,中项目才开始值得;全局状态也是从 useState 涨到 Context 再到状态库的。架构设计的成熟度要和项目规模匹配——给一个几百行的项目套大型架构,和给一个几十万行的项目不留架构,同样糟糕。

判断「现在该不该上架构」的问题:项目里有没有出现「找代码难、改一处崩三处、新成员上手慢」的痛点?有,架构的收益才开始大于成本;没有,保持简单。

常见疑问快答

「原子设计是不是太死板?项目真的按五层分吗?」 五层是「组织思维的参考系」,不是「必须建五个目录」。真实项目里最常见的是「三层简化版」:基础组件(原子+分子)、业务组件(组织+模板)、页面(页面层)。很多团队把「模板」并入「页面」,因为中小项目里两者边界模糊。原子设计的价值不在「严格分层」,而在「依赖方向」——基础组件不依赖业务、业务组件不依赖页面,这条线守住,分几层、怎么命名都行。判断一个项目分层合不合理,看「下层能不能被别处复用」:基础层组件换个项目能带走,就是健康的分层。

「目录按功能分(features)还是按类型分(components/hooks/utils)?」 这是前端架构的经典分歧。按类型分的好处是「一眼知道这是个组件还是工具」,坏处是「一个业务功能的代码散在四五个目录里」;按功能分的好处是「一个业务域自包含、改一处只动一个目录」,坏处是「跨功能复用的通用组件不知道放哪」。工程上主流的折中是「双轨制」:通用能力(components/hooks/api)按类型分,业务模块(features)按功能分,业务模块里的私有组件放模块内部。5.1 节推荐的目录结构就是这个思路——它是被大量中大型项目验证过的形态,别轻易发明新结构。

「状态提升到哪一层才算合适?」 规则是「找到最深的公共父组件」——两个组件都要用的状态,提升到它们共同的最近父组件;再往上如果还有组件要用,继续往上提。但「最近公共父」有时会引发「连锁重渲染」:状态提到很高层,中间所有层都感知这个状态。所以工程上还有一个补充判断:共享范围小(几个组件)用提升,共享范围大(整个页面或全局)考虑 Context 或状态库——提升是「组件树内」的方案,Context 是「跨层」的方案。第 2.5 节、第 3.3 节都讲了这条分界线,这里串起来:状态提升处理「局部共享」,Context/状态库处理「全局共享」,两条路按范围选。

「怎么让新成员快速理解项目架构?」 架构文档不如「目录即文档」。最好的方式是让目录结构本身成为说明——通用组件在 components、业务模块在 features、状态在 store、请求在 api,新成员顺着路径就能猜出「这是哪个业务的组件、数据从哪来」。比文档更有效的是「约定成文」:把目录规范、命名规范、状态分配原则写成一页纸团队约定,和新成员一起 review 几个实际组件。架构的传承不是靠「读代码」,是靠「先看地图再看代码」——目录就是地图,约定就是图例,两者齐了,上手速度会快很多。

要点速记

  • 原子设计五层:原子/分子/组织/模板/页面,依赖只往下流
  • 分层收益:改基础层全站生效,可移植性测试判断业务逻辑是否下沉
  • 三层架构:UI 层渲染、逻辑层 Hook、数据层 API
  • 设计三原则:单一职责、关注点分离、避免过度抽象
  • 目录结构:components 通用 + features 业务域,功能优先
  • 状态分配:局部优先、全局最少、服务器状态归请求库
  • 模块边界:模块间不互相访问内部组件,公共接口越少越好
  • 基线底线:可访问性与安全从第一天就做
  • 架构演进:复杂度与规模匹配,痛点出现时架构收益才大于成本

下一节进入项目开发实战——架构的每个决策在这里变成执行步骤。从需求拆解到部署上线,一条完整链路走一遍,前四章的知识在这里组装成真正的应用。这是整个教程的收尾,也是检验你掌握程度的终极演练。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U