White Meta
登录Admin
语言
中文EN
Theme: ...

© 2026 White Meta

回到顶部

返回文章列表

组件、Container 与 Store:前端状态边界该怎么划

前端项目写久了,很多人都会遇到一个别扭的阶段:

组件明明已经拆得很细,页面代码却越来越乱。

按钮、弹窗、表单、表格,一个个看上去都很“纯”,但一到真正接业务的时候,页面层开始塞满 props、事件回调和各种 hook。组件像一群嗷嗷待哺的孩子,页面像一个被迫同时照顾所有人的家长。

问题通常不是“拆得不够细”,而是状态边界没有划清。

先说一个常见误区:纯组件不等于万能组件

很多团队会强调组件要“纯”:

  • 只接收 props
  • 只负责渲染
  • 有交互就向外抛事件
  • 不直接碰业务状态

这套原则本身没错,而且对基础组件特别重要。

比如按钮应该只关心文案、样式和点击;弹窗应该只关心展示内容和开关;表单输入项应该只关心当前值与变更事件。

问题在于,如果把这套原则机械地推到所有层级,页面会开始承担过多责任。

于是你会看到这些症状:

  • 一个组件需要十几个 props
  • 一个页面里挂满了控制弹窗、表格、筛选、提交的 hooks
  • 组件本身看起来很干净,但业务逻辑被集中挤在页面层

这并不是“代码更清晰”,而是“复杂度被转移了位置”。

纯组件和页面之间,应该有一层中间地带

这层中间地带,很多人会叫它 container。

你可以把它理解成“业务容器组件”:它不像基础组件那样极致纯粹,但也不该膨胀成页面级大杂烩。

它的作用是把某一块相对内聚的业务状态和交互逻辑包起来,让页面层只负责拼装,让基础组件只负责展示。

这其实是在回答一个很现实的问题:

一段逻辑,到底该放进组件、页面,还是全局 store?

如果不引入 container 这一层,答案往往只剩两个极端:

  • 全都塞页面
  • 全都塞全局状态

而这两个极端都不好维护。

第一类 container:内聚型

内聚型 container 适合那种“自己就能闭环”的功能块,比如一个申请表单、一个上传面板、一个搜索框组合。

它通常同时管理:

  • 这块功能自己的局部状态
  • 表单校验或交互流程
  • 提交、重置、切换等操作

外部页面只需要把它当成一个相对完整的业务模块来使用。

这类 container 的好处是,状态和行为都围绕同一个目标组织,不会散落在页面各处。你甚至可以把它拆成 “UI 组件 + 自定义 hook”,既保留可测试性,又不至于让页面知道太多细节。

第二类 container:组合型

还有一类更常见的场景,是页面由很多“单点功能”组成,但这些功能之间其实存在明显关系。

例如一个复杂表格页:

  • 表格每行有多个操作按钮
  • 不同按钮会拉起不同弹窗
  • 弹窗之间共享部分状态
  • 页面上还要处理列表数据、筛选条件、权限控制

如果所有按钮、弹窗开关、回调函数都平铺在页面组件里,代码很快就会炸开。

这时更合理的做法,往往是按职责重新组合:

  • 一个 TableContainer 负责表格及其行操作
  • 一个 DialogGroup 负责所有相关弹窗
  • 页面只负责把它们放在一起

这样做不是为了“看起来优雅”,而是为了让相关逻辑待在一起。你以后改弹窗,不必先在页面里翻半天;你以后加一个按钮,也不必顺手再多塞两个 state 到页面根部。

那 store 应该放在哪一层

很多人一碰到复杂度,就想把状态直接扔进全局 store。

这招有时有效,但如果没有节制,副作用也很明显:本来只在某个局部功能里有意义的状态,被提升成了全局知识,最后谁都能读、谁都可能改,边界反而更模糊。

一个更稳妥的判断方式是:

  • 只影响当前功能块的状态,优先放 container 或局部 hook
  • 需要跨页面、跨模块共享的状态,再考虑进 store
  • 纯展示组件尽量不直接依赖 store

这样一来,store 真正承载的是“共享的业务事实”,而不是所有懒得传 props 的东西。

Context API 能不能顶上

如果项目不大,当然可以用 Context API 解决一部分状态共享问题,尤其是主题、语言、当前用户这类天然跨层级的信息。

但 Context 更像“传递机制”,不是完整的状态管理方案。

它最适合解决的是 props drilling,也就是属性逐层透传;一旦你把很多频繁变化的业务状态都塞进去,就会开始面对消费者重渲染、Provider 层层嵌套、数据流不易追踪这些问题。

所以 Context 能用,但不该默认承担所有共享状态职责。

本质上是在做职责分层

把组件、container、store 分开,不是为了发明新名词,而是为了回答三个问题:

  • 这段逻辑是展示逻辑,还是业务逻辑
  • 这份状态是局部状态,还是共享状态
  • 这块复杂度应该被封装在哪一层

一旦这些边界清晰,代码会出现一个很明显的变化:不是代码量变少了,而是“每一层只承担自己该承担的复杂度”。

这比单纯追求“组件越纯越好”更接近真实工程。

结语

好的组件系统,不是把所有组件都训练成只会接 props 的乖孩子,而是知道什么时候该纯,什么时候该内聚,什么时候该提升为共享状态。

如果页面已经开始变成“所有逻辑的垃圾回收站”,那往往不是你拆组件拆少了,而是缺了一层 container,或者把本该局部处理的状态过早塞进了全局。

状态边界一旦划清,组件才会真正变得好用,页面也才会重新变得轻松。