[译]展示组件(Presentational Components)与容器组件(Container Components)

原文地址

我发现当你在写React应用时,将组件分成容器组件与展示组件是一个好模式。如果你使用react已有一段时间了,你可能早就发现这件事了。这里有一篇文章对其进行了很好的解释,但我想补充几点。

如果你将组件分成这两类的话,你会发现它们变的更容易复用与理解。本文中这两类被称为展示组件与容器组件。但是你可以在其他文章中读到别的称呼如: Fat 与 Skinny,Smart 与 Dumb,Stateful 与 Pure,Screens 与 Components等。它们并非完全相同,但核心思想是类似的。

展示组件的特点:

  • 关注UI呈现。

  • 内部可以包含展示组件/容器组件,通常包含DOM标签并拥有自己的样式。

  • 可以通过this.props.children包含其他组件。

  • 不依赖于app的其余部分,如Flux的actionstore

  • 不定义数据如何加载与变化。

  • 仅通过props接收数据与回调函数。

  • 即便拥有状态也只是UI的状态而非data。

  • 若非需要拥有状态,生命周期的钩子函数或性能优化,通常以函数组件方式呈现。

  • 例子:Page,Sidebar,Story,UserInfo,List

容器组件的特点:

  • 关注运作。

  • 内部可包含展示组件/容器组件,但通常并没有DOM标签与任何样式(除非外层需要包裹一层div)。

  • 为展示组件/容器组件提供数据与行为。

  • 调用Flux的action,将其作为回调函数传给展示组件。

  • 通常是有状态的,因为往往被作为数据源使用。

  • 通常由高级组件而非手写生成。高级组件包括Redux的connect(),Relay的createContainer()或Flux的Container.create()。

  • 例子:UserPage,FollowersSidebar,StoryContainer,FollowedUserList

我将它们放在不同的项目文件夹下以便更好地区分它们。

这个过程的好处

  • 更好地分离关注点。通过这种方式编写组件,你能更好地理解你的app与UI。

  • 更好的复用性。你可以使用带有完全不同数据源的同一展示组件,并将其放入独立的容器组件中。

  • 展示组件可以称为app的“调色板”。你可以将其放在一个页面中让设计来随意折腾而不会改变app的逻辑。在页面上你可以进行页面快照的回归测试。

  • 这样迫使你去分离像Sidebar,Page,ContextMenu这样的“布局组件”,并使用this.props.children,用这种方法来替代在不同容器组件中重复复制同样的标签。

谨记,组件并非一定要构建DOM。它们区分UI之间的边界并给予一种组合UI的方式。你需要善用这一点。

何时引入容器组件?

我的建议是先仅使用展示组件来构建app。最后,你会发现你往中间组件中传入太多props了。这时你会意识到一些组件并不需要使用它们接收到的props而只是把他们往下一层传递,并且在子组件需要更多数据时,你需要从新连接所有这些中间组件,这时就是引入容器组件的正确时机了。这样子组件可以获取数据和行为props而不必通过中间那些不相关的组件。(译注:简单来说就是把这些状态拿出来统一管理而非层层传递)

这是一个边写边改的过程,所以不要期待一蹴而就。在尝试这种模式时,你能逐渐产生何时需要引入容器组件的直觉,就像你知道何时使用函数一样。
这个免费的Redux入门系列也许能帮到你

一些名词的比较

须知展示组件与容器组件并非存在技术性差异,它们的区别在于使用目的。

相对地,以下列举一些相关(但不相同)的技术性差异:

  • 有状态(Stateful) 与 无状态(Stateless)(译注:这里指的是组件的状态)。有使用React的setState()方法的组件,也有不使用该方法的组件。是否容器组件往往是有状态的,而展示组件是无状态的?这并非绝对。容器组件也可以是无状态的,反之,展示组件也可以有状态。

  • 类(Classes)与函数(Functions)。从React 0.14起,组件即可以使用类方式申明也可以使用函数的方式。函数组件申明更简单但缺少类组件所拥有的一些特性。其中一些限制也许在未来会消失,但目前它依旧存在。函数组件因其更易理解,所以我建议你优先使用,除非你需要状态,生命周期钩子函数或性能优化,这些功能目前只有类组件才拥有。

  • 纯(Pure)与非纯(Impure)。对于那些每次给予相同props与state能保证输出相同结果的组件,我们认为其是纯的。纯组件可以是类组件/函数组件,也可以有状态/无状态。由于纯组件并不依赖 props 或 state的深层变化,它的渲染性能可以通过在生命周期shouldComponentUpdate中对props/state进行浅层比较来优化。目前,仅有类组件可以定义shouldComponentUpdate方法,但将来也许会有所改变。

以上并非区分展示组件与容器组件的方式。在我的经验来看展示组件更多是无状态的非纯的函数组件而容器组件往往是有状态的纯的类组件,而这并非规则,仅仅是观察得出来的,并且在特定的条件下,我也见相反的情况。不需要教条地区分展示组件与容器组件。有些时候并不需要叶很难划出一条分界线。如果你对一个具体组件应该是容器组件还是展示组件感到疑惑,那说明此时区分还尚早。继续做下去。

例子:
Michael Chan这篇文章一针见血。

深入阅读:

脚注

在我的早期文章中,你会发现我使用“smart”与“dumb”组件的方式来称呼它们。但是现在看来是不恰当的,更重要的是这并不能反映它们在使用目的上的差异。我觉得使用展示组件与容器组件会更合适。
并且之前的文章中,我认为展示组件只能包含其他展示组件,现在看来也不合理。是包含展示组件/容器组件是实现上的细节。在不改变调用位置的情况下,你应该能够用容器组件替换展示组件。因此展示组件既能包含容器组件也能包含展示组件,容器组件亦然。

    原文作者:master_yoda
    原文地址: https://segmentfault.com/a/1190000009955800
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞