12月16, 2017

【译】展示组件vs容器组件

alt

当我写React应用程序的时候,我发现了一个很有用的简单模式。如果你已经使用了一段时间React框架,那可能你也已经发现了这种模式。这篇文章很好的解释了这个问题,但是这里我想增加一些知识点。

在开发过程中,如果你将组件拆分为两类,你会发现会更有利于组件的复用。这里,我把它们称为容器组件展示组件,当然也有人称之为胖组件瘦组件轻组件重组件有状态组件无状态组件屏幕组件普通组件等等。称呼不尽相同,但是核心思想都差不多。

我认为的展示组件:

  • 更关注于如何展示。
  • 可能同时包含展示组件和容器组件,通常有自己的DOM标签和样式。
  • 允许容器通过this.props.children传递。
  • 对应用程序的其他部分,例如:Flux 中的action 和 store,没有依赖性。
  • 不需要关注数据如何加载和变化。
  • 只能通过props接收数据和回调函数。
  • 很少有自己的状态(如果有的话,也是UI的状态而不是数据的状态)。
  • 通常会被编写成函数式组件,除非它们需要状态,生命周期钩子或是性能优化。
  • 比如:Page, Sidebar, Story, UserInfo, List(译作:分页,侧边栏,文章,用户信息,列表)。

我认为的容器组件:

  • 更关注于如何工作
  • 可能同时包含展示组件和容器组件,但是通常没有自己的DOM标签和样式,最多包含一个外层div包裹容器
  • 将数据和行为提供给展示组件或其他容器组件
  • 调用Flux action并将其作为回调函数提供给展示组件
  • 充满状态,更倾向于作为数据源
  • 通常由更高阶的组件,如React Redux中的connect(), Relay中的createContainer(), Flux Utils中的Container.create(),生成的而不是手写出来的组件。
  • 比如:UserPage, FollowersSidebar, StoryContainer, FollowedUserList(译作:用户页面,侧边栏监听器,文章容器,用户列表监听器)

这种方式的好处

  • 更好的区分概念。通过这种方式来写组件,你可以更好的理解你的应用程序和你的UI。
  • 更好的复用性。你可以使用相同的展示组件到完全不同状态的数据中,把它们转换成可以进一步复用的独立的容器组件。
  • 展示组件实际上是你应用的“调色板”。你可以把他们放在一个独立的页面中然后让设计师来调整所有的变化,整个过程中都不需要涉及应用程序的逻辑。你可以在那个页面上进行截图回归测试。
  • 这就迫使你不得不提取“布局组件”,如侧边栏,页面,上下文菜单,并使用this.props.children,而不是在几个容器组件中重复相同的标签和布局。

请记住,组件不需要产生DOM,它们只需要提供UI概念之间的组成模块。

要学会利用这一优势。

什么时候引入容器?

我建议你先只用展示容器开始构建你的应用程序。终于,你意识到向下传递太多的props到中间组件。当你注意到有些组件其实并没有使用他们接收到props,而只是作为中间层进行传递,并且在这过程中,任何时候孩子节点需要更多的数据时,你都不得不重新连接所有的中间组件,这时候就可以引入容器组件的概念了。这样就可以在不增加树结构中不相关中间件负担的基础上,获取到数据和行为。

这是一个进阶式的重构过程,所以不要奢望第一次就可以做对。当你尝试使用这种模式时,你就对什么时候提取容器组件形成一种感观,就像你知道什么时候提取公用函数一样。这篇文章也许也可以帮助到你。

其他的区别

有一个点很重要,那就是你要明白展示组件和容器组件的区别不是技术上的。相反,这是基于它们的目的进行区分的。

相比之下,这里有一些相关的(但不同的)技术区别:

  • 有状态和无状态。一些组件使用React中的setState()方法但是有一些却没有。容器组件更趋向于有状态而展示组件更趋向于无状态,但这不是一个硬性规定。展示组件也可以是有状态,容器组件也可以有很少的状态。
  • Class 和 Function。从React 0.14版本起,组件就既可以使用class来声明和可以使用function来声明了。函数式组件更易于定义但是目前只有类组件才具备的功能。这些限制将来可能都会消除但是现在还是存在的。因为函数式组件更容易理解,我建议你使用它们,除非你需要状态,生命周期钩子,或性能优化,那你只能在这时候使用类组件了。

  • 纯组件和非纯组件人们总是说如果一个组件可以确保在任何时候输入相同的props和state时都返回相同结果,那就可以称之为纯组件。纯组件既可以通过函数的方式定义,也可以通过类的方式定义,既可以是有状态的,也可以是无状态的。另外一个重要优势是,纯组件不依赖于state和props的深层变化,所以他们的渲染性能可以通过shouldComponentUpdate() 进行浅比较来进行优化。现在只有class中可以定义shouldComponentUpdate() ,也许将来也会改变。

不管是展示组件还是容器组件都可能掉入思维的惯性中。在我的经验中,展示组件趋向于无状态的纯函数,容器组件趋向于有状态的纯class。然鹅,这并不是一个绝对的规定而是一个建议。我也曾见过在特定情况下完全合理的相反情况。

所以不要把展示组件和容器组件教条的进行区分。有时候这很难做出明确的区分来。如果你觉得你先没办法对一个组件到底是展示组件还是容器组件做区分的话,也许是现在决定的时机为时尚早。别紧张!

扩展阅读:

脚注

*在这篇文章更早的版本中,我称他们为“smart”和“dumb”(译:“轻”和“重”)组件,但这对于展示组件来讲,有些苛刻,最重要的是没有真正的解释它们的目的所在。相比之下我更喜欢新的定义,我希望你们也喜欢!

**在这篇文章更早的版本中,我声明过组件应该只包含展示组件。现在我不这么认为了。不管一个组件是展示组件还是容器组件,都是它的实现细节。你可以通过不改变容器组件的任何调用栈的方式来代替展示组件。因此,展示组件和容器组件都可以包含其他的展示组件或容器组件的。

原文链接:https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0

本文链接:https://www.imwineki.cn/post/PresentationalandContainerComponents.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。