展示组件和容器组件
我发现一个在编写 React 应用时十分有用而且简单的模式。如果你已经编写 React 很久了那你可能就会发现这个模式。这篇文章描述的十分好,但是我想加入一些我个人的观点。
如果你将组件分成两类的话,你会发现他们更易于重用和理解。我称他们为表现组件和容器组件但是我还听说过这些形容词比如重或轻,聪明或愚蠢,有状态和无状态,展示和组件。他们不完全相同但是核心思想是一样的。
我的展示组件:
- 关注组件是如何显示的。
- 可以嵌套展示组件和容器组件,有时候还有一些自身的 DOM 节点和样式。
- 允许使用
this.props.children
。 - 不依赖于 app 的其他内容,比如 Flux actions 或是 store。
- 不用指定数据是如何加载或变化的。
- 只从 props 中获取数据和回调函数。
- 几乎没有他们自己的状态。(除非是 UI 的交互状态而不是数据状态)。
- 一般使用纯函数创建组件除非他们需要状态,生命周期或性能优化。
- 例子:页面,侧边栏,新闻,用户信息,列表等。
我的容器组件:
- 关注组件是如何工作的。
- 可以嵌套展示组件和容器组件,但是基本不会有 DOM 节点除了一些包裹用的 div,并且拥有不会有样式。
- 给其他展示组件或容器组件提供数据和行为。
- 调用 Flux actions 并将它作为回调函数提供给展示组件。
- 基本是有状态的,并且往往作为数据源。
- 经常使用高阶组件,比如 React Redux 的
connect()
,Realy 的createContainer()
,或者是 Flux Utils 的Container.create()
,而不是手写。
分类组件的益处
- 更好的关注点分离。通过这个方法构建组件你可以更好的理解你的 app 和你的 UI。
- 更好的重用性。你可以将完全不同的数据运用到你的组件上,如果将数据分散到各个容器组件中能够进一步的进行重用。
- 展示组件本质上来说是你 app 的“调色盘”。你可以将它们放到一个页面中,然后让设计师进行各种内容的微调且不影响 app 的逻辑。你可以在这个页面上运行截图的回归测试。
- 可以迫使你提取“布局组件”例如,侧边栏,页面,菜单并且可以使用
this.props.children
来替代容器组件中重复的节点和布局。
记住,组件并不只是由 dom 去构建。他们仅仅是用来提供 UI 关注点的边界成分。
试着去使用它。
何时引入容器组件
我建议你在创建你的 app 时先编写展示型组件。最后你会发现你向中间层组件传入了过多的参数。当你发现一些组件接受了他们不需要的参数并只需要向下传递给他们的子组件而不停的更新代码时,这是一个引入容器组件的很好时机。通过这个方法你可以让子组件获取数据和行为参数时,中间层组件不需要承担整个组件树里不相关的数据传递。
这是一个持续迭代的过程,不要想着在第一次实现的时候就完成。经过一段时间的这种模式,你就能凭借直觉提取出容器组件,就像你抽象出一个方法一样。我的免费的 Redux 理论教学系列 可以帮助到你!
其他的分类方法
展示型组件和容器型组件之间的区别很重要的一点是他们不是一个技术点,而是他们被创建的目的。
作为参照,下面有一些相关的技术区别(但是是不同的!):
有状态和无状态。一些组件使用 React 的
setState()
方法有的不使用。然后容器组件是有状态的而展示型组件是无状态的,这不是一个硬性的规则。容器组件可能也是无状态,展示型组件可能是有状态的。类和方法。从 React 0.14 开始。组件可以被声明为类或者方法。纯函数组件的定义更简单因为他缺少一些使用类生成组件的特性。这些限制在将来可能会被移除但是现在还是存在的。因为纯函数组件更容易理解,我建议你使用它除非你需要用到状态,生命周期或者性能优化这些类组件的特性。
纯组件和非纯组件。大家所说的纯组件是那些给予指定的状态必定会返回相同显示的组件。纯组件既可以被定义为函数式组件也可以被定义为类组件,同时拥有状态或没有状态。另一方面纯组件不依赖于很深的数据变化和状态。所以他们可以通过浅比较从而使用
shouldComponentUpdate()
方法进行优化。目前只要类组件可以使用这个生命周期,但是可能在未来改变。
展示型组件和容器组件都可以进行混用。在我的经验里,展示型组件应该是一个纯净的纯函数,容器型组件应该是一个状态的纯类。然后这不是一个规则而是一种现象,我曾经在一些特定的情况下看到完全相反的情况。
不要将展示型组件和容器组件的区别作为一个教条。基本上很难分清界限也没有很大的问题。如果你还无法区分一个组件应该是展示型组件还是容器组件,那对于你来说讨论它还太早了,不用过于着急!