容器组件和展示组件(翻译)

作者:Dan Abramov 原文链接:Presentational and Container Components

在用 React 写应用的时候,我发现了一种非常好用又简单的模式。如果你已经使用过 React,很可能你早就发现它了。这篇文章讲得很好,但是,我还是想补充几点。

当你尝试把组件分成两类后,你会发现它们更容易被重用和理解(reuse and reason)。我将他们分为容器组件展示组件(Container and Presentational components)。我也听过其他说法,比如“胖的"和"瘦的"(Fat and Skinny),"智能的"和"单调的"(Smart and Dumb),"多状态变量"和"单纯的"(Stateful and Pure),"封装物"和"元件"(Screens and Components),等。这些概念并不完全一样,但核心思想都是一致的。

我所说的展示组件(My presentational components):

  • 只关心它们的样子(how things look)。
  • 可能同时包含子级容器组件和展示组件,一般含 DOM 标签和自己的样式。
  • 通常用this.props.children来包含其他组件
  • 不依赖 app 其它组件,比如 flux 的 actions 和 stores
  • 不会定义数据如何读取,如何改变
  • 只通过this.props接受数据和回调函数
  • 很少有自己的状态变量,即使有,也是 UI 的状态变量,比如toggleMenuOpenInputFocus
  • 一般是函数级组件,除非它们需要状态,lifecycle hooks,优化处理。
  • 例子:Page,Sidebar,Story,UserInfo,List。

我所说的容器组件(My container components:):

  • 只关心它们的运作方式( how things work)。
  • 可能同时包含子级容器组件和展示组件,但大都不含 DOM 标签,而含他们自己所用的 wrapping div,从不用自己的样式。
  • 为展示组件或其他组件提供数据和方法。
  • 调用 Flux 的 actions,并且将其作为展示组件的回调函数。
  • 维持许多状态变量,通常充当一个数据源。
  • 通常由高阶组件生成,比如 Redux 里的 connect(),Relay 里的 createContainer(),Flux Utils 里的 Container.create(),而非手工写出。
  • 例子:UserPage, FollowersSidebar, StoryContainer, FollowedUserList。

我把他们放在不同的文件夹中,以示区别。

这种方法的好处(Benefits of This Approach)

  • 更好的分离关注,用这种方式写组件,你可以更好的理解 你的 app 和 你的 UI。
  • 更易复用,同样的展示组件可以在不同的状态源、数据源中使用。也可以封装成容器组件,在未来重用它们。
  • 展示组件是 app 的调色板。你可以把它们放到单独的页面,并让设计师来调整它们的样式和结构,而不用改变 app 的逻辑。单独的页面有静态性,你可以在上面进行 screenshot regression 测试。
  • 这会强迫你去抽象出layout components,比如 Sidebar, Page, ContextMenu,强迫你去使用 this.props.children,而非在不同容器中不断复制markup and layout

记住,React 的组件不一定要生成 DOM,它们只需要考虑如何设计 UI 之间的分界与组合关系。

好好利用一这点。

什么时候引入容器?(When to Introduce Containers?)

我建议你做 app 的时候优先使用展示组件。当你意识到,有一些中间组件传递了过多的 props,有一些组件并不使用它们继承的 props 而只是将这些 props 传递给他们的子级,而且每次子级组件需要更多数据时,你都需要重新调整或编写这些中间组件,那么,这时候你可以考虑引入容器组件了。这样做,你可以传递 props 和方法给末端的子级组件,而不必麻烦一些不相关的中间组件。

这是一个持续的重构过程,所以不要试图第一次就把它做好。你尝试着这种模式,慢慢地你会培养起一种直觉,知道何时引入容器,就像你知道何时提取函数一样。我的免费redux 教程也许会帮助到你。

其他二分法(Other Dichotomies)

容器组件和展示组件的分别并不被严格定义,理解这一点很重要。为了对比,我再列举一些相关(但是不同的)的二分法。

多状态变量和少状态变量(Stateful and Stateless)

有些组件用setState(),有些不用。容器组件通常多状态变量,而展示组件却不,这不是铁规律。展示组件也可以多状态变量,而容器组件也可以少状态变量。

类与函数(Classes and Functions)

自从 React0.14,组件可以被声明为类,也可以被声明为函数。定义函数方便,却少了类独有的特性。有些限制或许会在未来消失,但是它们至少是存在的。因为函数容易理解,所以我推荐使用函数,除非你需要那些,现在只有类才有的状态管理,lifecycle hooks,性能优化等特性。

纯的不纯的(Pure and Impure)

人们说一个组件纯,是指给予它一样propsstate,它保证能输出一样的结果。

纯函数可以是类,可以是函数,可以多状态,可以无状态。

Another important aspect of pure components is that they don’t rely on deep mutations in props or state, so their rendering performance can be optimized by a shallow comparison in their shouldComponentUpdate() hook。

目前只有类可以定义shouldComponentUpdate(),或许将来有改变吧。

展示组件和容器组件都有上述的二分特性。

在我看来,展示组件倾向于少状态、纯的函数,容器组件倾向于多状态,纯的类。

当然啦,这只是个人观察,而非规则,我也见过完全相反的情况。

不要将展示组件和容器组件当作教条。有些时候,不必划出清晰的线条,也不用觉得划出区分会很困难。如果你分不清某个组件是展示组件还是容器组件,也许是为时尚早。别着急!(Don’t sweat it!)

Footnotes

在本文的早期版本中,我将它们称为 smart 组件和 dumb 组件,但这对 presentational components 来说太苛刻了,更重要的是,这并没有真正解释它们的目的的不同。我更喜欢新的专业术语,我希望你们也喜欢!

在本文的早期版本中,我声称 presentational components 应该只包含其他 presentational components。我认为情况不再是这样了。componentpresentational component 还是 container 是其实现细节。你应该能够在不修改任何调用栈的情况下,用 container 替换 presentational component。因此,presentational componentcontainer components 都可以包含其他 presentational or container components

——完——

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容