原文:Presentational and Container Components
Presentational
组件
- 和UI有关
- 可能包含
presentational
和container
组件,通常有DOM标记或者本身的样式信息。 - 允许通过
this.props.children
传入内容。 - 没有对项目中其他的依赖,例如
Flux
actions 和stores。 - 不会指定数据是如何加载或者变更的。
- 分别通过props接受数据和回调函数。
- 很少有自身的
state
() - 一般是函数式组件的形式除非他们需要state,声明周期钩子,或者性能优化。
- 例如:Page, Sidebar, Story, UserInfo, List.
Container
组件
- 和行为/逻辑有关系。
- 可能包含
presentational
和container
组件,但是没有DOM标记,除了一个包装div
,没有任何的样式信息。 - 为
presentational
组件或者其他的container
组件提供行为和数据。 - 调用
Flux/Redux
的actions
,提供回调函数给presentational
组件。 - 有状态的,作为数据源的提供者。
- 一般是通过高阶组件的方式生成,例如React Redux的
connect()
方法,Relay的createContainer()
,Flux的Container.create()
方法。 - 示例:UserPage, FollowersSidebar, StoryContainer, FollowedUserList.
Benefits of This Approach
- 更好的关注点分离。更好的理解你的应用和UI,通过这种方式。
- 更好的复用性。可以把相同的
presentational
组件应用在不同的container
组件里,也可以应用不同的数据源。 Presentational
组件是应用的‘调色板’。你可以把他们放在一个单独的页面,让设计者调整它们的变量,而不需要修改应用的逻辑。你可以在这个页面进行快照回归测试。- 这会强迫你抽取
‘layout component’
例如Sidebar, Page, ContextMenu,在一些container
组件里用this.props.children
来代替复制相同的标记。
记住,组件不是来生成DOM。他们只是在**_UI_**
关注点之间提供组合边界。
什么时候引入Container
?
我建议你一开始构建应用的时候,先从presentational
组件开始。最终你会发现你传递了太多的props
给中间组件。当你注意到很多的组件并不需要一些接收到的props
,仅仅是向下转发,你需要重新连接这些中间组件,而子组件需要更多的数据,这个时候你就要考虑引入一些container
组件了。这种方式你可以把包含数据和行为的props
传递给叶子组件,而不需要给组件结构树中间的组件增加负担,传递不需要的props
。
重构是实时进行的,没必要第一次就把它做对。随着体会到这种模式的好处,你会开发出一种直觉就是知道何时去抽取container
,就像你知道该何时去抽取函数一样。
其他的分类
这是非常重要的,理解presentational
组件和container
组件之间的区别并不是技术上的区别。而是使用目的上的区别。
相比之下,也会有一些技术上的差别:
有状态和无状态。一些组件会用到React
setState()
方法而其他的则用不到。container
组件一般是有状态的,而presentational
一般是无状态的,但这没有严格的限制。presentational
组件也可以是有状态的,而container
组件可以是无状态的。类组件和函数组件。自从React0.14的发布,组件既可以被声明为函数组件也可以是类组件。函数式组件定义比较简单,但是缺乏一些必要的特性,目前只是应用在类组件里。由于函数式组件是易于理解的,应该在项目中尽可能多的使用他们。
纯组件和非纯组件。组件是纯洁的当给予相同的
props
和state
,它保证返回相同的结果。纯组件可以是类组件也可以是函数组件,也可以是有状态的也可以是无状态的。对于纯洁组件另一个重要的方面是不依赖于props
和state
深度变更,所以他们的渲染性能可以通过浅层比较优化通过shouldComponentUpdate
钩子方法实现。
Presentational
和Container
组件可以是两者皆可的。从我的经验,Presentational
组件通常是无状态纯函数组件,Container
组件通常是有状态纯Class组件。
其他参考: