32

注意:为简单起见,将组件深度视为:

- Smart (grand)parent level 0
  - dumb child level 1
   ....
    - dumb grandchild level 2
      ....)

关于智能/大/父/子组件如何在多级(至少 3 个级别)链上进行通信和传递数据的方式有多种选择和条件。我们希望将我们的“智能”(祖)父组件保留为唯一可以访问我们的数据服务(或原子/不可变存储)的组件,它将推动与“哑”(孙)子级的信息交换。我们看到的选项是:

  1. 反模式(?):通过@Input/@Output 绑定在组件链上下传递数据。这就是一些人所说的“无关属性”或“自定义事件冒泡问题”(例如:这里这里。)。不去。
  2. 反模式:智能组件通过 @ViewChildren 或 @ContentChilden 访问哑(大)子级。这再次硬连线孩子,仍然没有为(大)孩子创建一个干净的机制来将数据向上传递给智能组件。
  3. 共享消息服务,如 angular.io 食谱中所述,这里是一篇优秀的文章
  4. ?

现在在“3”的情况下,哑(大)子必须注入消息服务。这让我想到了我的问题:

Q1:对于每个“笨”(孙)子来说,注入消息服务在直觉上似乎很奇怪。消息服务的最佳实践是为这个家庭提供专用服务,还是搭载上面提到的“智能”祖父母负责的数据服务?

Q1A:此外,如果所有组件都将注入服务,这比在链上上下添加@Input/@Output 绑定要好得多吗?(我看到“愚蠢”组件需要某种方式来获取信息的论点)

Q2:如果“聪明”的祖父母正在与类似 redux 的商店(我们的 ngrx)进行通信怎么办?再一次,与“哑”组件的通信最好通过注入/专用消息服务进行,还是最好将存储注入每个“哑”组件......或者?请注意,组件间通信是“动作”(例如:表单验证、禁用按钮等)和数据(即,将数据添加到/更新存储或服务)的组合。

想法非常感谢!

4

3 回答 3

11

(更新:2019 年 2 月 7 日:这篇文章已经过时了——添加了“商店/ngrx”模式)

因此,在进一步研究了这一点之后,当谈到如何最好地向下和向上通信嵌套组件链时,似乎真的只有两种选择——浮士德式的讨价还价:

任何一个

  • 向上、向下和整个嵌套组件链传递@Input/@Output 绑定(即处理“自定义事件冒泡”或“无关属性”的问题)

或者

  • 使用消息/订阅服务在这一系列组件之间进行通信(这里有很好的描述),并为链中的每个组件注入该服务。

或者:

  • 反应式存储模式(例如“ngrx”)是另一种选择。请注意,IMO,智能组件和哑组件的概念仍然适用。也就是说,哑组件永远不会直接访问商店。同样,智能组件是通过商店获取数据的主要部分。

我个人是使用智能和演示(“哑”)组件的支持者。添加“商店”也应该有选择地完成,因为它显着增加了从架构、一致的实施模式、开发和维护到新员工入职的流程成本。名义上,一个“哑”组件只需要@Inputs 和@Outputs,就是这样。它不关心它在组件树中有多深或多浅——这就是应用程序的问题。事实上,它并不关心什么应用程序首先使用它。同时,如果将特定于应用程序的服务注入其中,则深层组件不是很笨拙或可移植的。顺便说一句,对方“聪明” 组件实际上是向其家族树中需要它的任何哑组件提供中间服务(通过一流的@Injectable 服务或类似 redux 的存储)。只要孙子以某种方式发出需要采取服务/存储操作的信号(再次通过@Input/@Output 链),智能组件也不关心其直接子代@Inputs 之外的组件。通过这种方式,智能组件也可以跨应用程序线进行传输。

鉴于此,浮士德式的交易 IMO 倾向于使用 @Input/@Output 链来解决它带来的所有上述问题。也就是说,我一直在关注这一点,如果有人知道的话,我欢迎干净和解耦的替代方案。

于 2017-05-09T01:07:45.883 回答
1

为什么#1 是反模式?祖父组件拥有数据并通过@Input 参数将其传递给哑子组件。当事件发生时(通过@Output 事件发射器),哑子组件简单地调用回调,从而导致祖父组件操作数据。对我来说似乎很干净。

编辑:我看到您关于通过许多中间层重复传递值(如提交处理程序)的观点。也许可以在父组件中创建代表您的组件树的嵌套结构。然后每个组件可以传递它需要的属性,加上一个对象传递给下一个组件。然后每个组件只知道它下面的一个:

// Parent component builds this object (or gets a service to do it)

viewModelForChildComponent: {

    property1NeededForChildComponent,

    property2NeededForChildComponent,

    viewModelForGrandChildComponent: {
        property1NeededForGrandChildComponent,

        property2NeededForGrandChildComponent,

        viewModelForGrandGrandChildComponent: {
            property1NeededForGrandGrandChildComponent,

            submitHandlerNeededForGrandGrandChildComponent
        }
    }
}
于 2017-05-07T15:38:03.720 回答
1

Input() 和 Output() 绑定也是一种完全合法的处理方式。让智能组件处理生成值的逻辑,然后使用 Input() 和 Output() 沿着组件链简单地传递和接收值。

当然,这指出了 smart/view 方法的缺点之一:更多的文件;更多样板。这就是为什么我不会主张一种万能的单一方法。相反,选择一种在您当前环境中有意义的方法(对于应用程序和您的组织)。

于 2017-05-07T18:16:37.470 回答