我找到了一个当我们在写react
应用时非常有用的模式。如果你写过一段时间的react,你可能已经发现了它。这篇文站很好的解释了它,但是我想补充一些观点。
如果将组件划分为两类,你会发现组件重用起来更加容易。我把这两类称为Container
和Presentational
。也有其他叫法,Fat
和 Skinny
, Smart
和 Dumb
, Stateful
和 Pure
, Screens
和 Components
等等。这些都不完全相同,但是核心思想是相似的。
我的presentational
组件:
- 关心事物如何展示;
- 也许会同时包含展示类组件和容器组件,并且通常有一些DOM操作和自己的样式;
- 通常允许
this.props.chidren
放在容器里; - 对应用的其余部分没有依赖,比如
Flux actions
或者stores
; - 不会说明数据是如何加载和变化的;
- 只通过
props
接受数据和回调函数; - 几乎没有自己的
state
(即使有,也是UI
相关的,而不是data
相关的); - 通常被写作函数式组件除非需要状态、生命周期的钩子、性能优化;
- 例子:
Page
,Sidebar
,Story
,UserInfo
,List
。
我的container
组件:
- 关心事物如何运作;
- 也许会同时包含展示类组件和容器组件,但是除了一些用来包含元素的div通常不会其他有DOM操作,并且不会包含任何样式;
- 为
presentational
组件或其他container
组件提供数据和行为; - 通常是有状态的,因为它们往往作为数据源;
- 通常使用像
React Redux
的connect()
、Relay
的createContainer()
、Flux Utils
的Container.create()
这样的高阶组件来生成,而不是手动编写; - 例子:
UserPage
,FollowersSidebar
,StoryContainer
,FollowedUserList
。
这种方式的好处:
- 更好的关注分离。通过这种方式编写组件,你能更好的理解你的应用和试图;
- 更好的可重用性。你可以将相同的
presentational
组件和完全不同的状态组合在一起,并将这些转化为可进一步重用的独立的容器组件; presentational
组件本质上是你应用的“调色盘”。你可以将它们放在一个单页面,设计师不接触应用的逻辑就可以调整所有的变化;- 这迫使你提取你的布局组件比如
Sidebar
,Page
,ContextMenu
,使用this.props.children
而不是在几个容器组件之间复制标签和布局。
什么时候引入Containers?
我建议你一开始只用presentational
组件来构建应用。最后你会意识到通过中间组件传递了太多的props
。当你注意到一些组件并不使用它们收到的props
而几乎只是向下传递,并且当子组件需要更多数据的时候你得重写所有的中间组件,这就是应该引入container
组件的好时候了。通过这种方式,你可以通过叶组件得到需要的数据和行为,而不会加重组件树中不相关的组件。
这是一个持续重构的过程,所以不要尝试一次就做的好。当你尝试这种模式,你会形成一种什么时候该提取一个container的直觉,就像你知道什么时候该提取一个函数一样。我的free Redux Egghead series也能在这方面帮助你。
其他分类
重要的是要理解presentational
组件和container
组件之间的区别不是一个技术问题,相反,区别在于它们的目的。
相比之下,这里有一些相关(但不同!)技术的区别:
- 有状态的和无状态的。一些组件使用
React
的setState()
方法但有些不使用,当presentational
组件往往是无状态的,container
组件往往是有状态,这不是一个硬性的规则。presentational
组件可以是有状态的,container
组件也可以是无状态的。 - 类和函数。从
React0.14
开始,组件可以以函数或类的方式声明。函数式组件定义更简单,但是缺乏一些类特有的功能。这些限制也许在未来会解决但是如今确实存在。由于函数式组件更容易理解,所以我建议你使用函数式组件,除非你需要状态、生命周期钩子和性能优化这些目前只提供给类的功能。 - 纯和不纯。如果给一个组件输入相同的
state
和props
,保证会输出相同的结果,大家称这样的组件为纯组件。纯组件可以使用函数或类来声明,并且可以是有状态的或无状态的。纯组件另一个重要的方面是它们不依赖props
和state
的深度变化,所以它们的渲染性能可以在shouldComponentUpdate()钩子函数里通过浅比较进行优化。目前只支持在类中定义shouldComponentUpdate()
,但是未来也许会改变。
presentational
组件和container
组件都可以是任意一种状态。以我的经验,presentational
组件往往是无状态的纯函数(pure functions),container
组件往往是有状态的纯类(pure classes),然而这只是观察得来的结论而不是一种规则,我曾经也看到过完全相反但是合理的情况。
不要把分离presentational
组件和container
组件当做一种教条。有时候并没那么重要或很难划清界限,如果你不确定一个组件该是presentational
组件或container
组件,也许是决定的太早了。别担心!