实际上我们要解决的场景一共有3种:
- 子组件从一开始就被设计为与某个固定的父组件挂钩,如上面的
CheckList和CheckItem的关系,是强耦合的 - 父组件需要某些子组件的通知来完成自己的逻辑,但子组件是任意的,如上面
Dialog里的close这个slot,是半透明的 - 父组件通过slot容纳子组件,有可能需要子组件的某些通知,但并不关心子组件是什么,且需要让子组件的通知了对有被更上层接收到,如上面的
TogglePanel(完全透明)和PersistForm(带有自己逻辑的透明),是透明的
我的设计是,让所有在slot中的组件的事件均会调用parentComponent#onSlotEvent,提供当前的事件对象和slot的名称,由父组件选择要如何处理
这种情况比较容易处理,无论是使用事件还是让子组件自己找到父组件进行注册都可以,因为两者均知道对方是什么有哪些行为,所以很容易就能形成一种协议来进行通信
这种情况下,通过onSlotEvent能接收到通知,然后完成自己的逻辑即可,这个逻辑可能还包含继续触发自己的事件
这一情况下,父组件需要有目的性地在onSlotEvent中调用this.fire(event)将事件直接代理出去
事实上,全透明才是比较罕见的情况,不然我们也不会直到今天才开始讨论这个问题了
这个方案的一个特点在于:
- 无论是哪一种情况,决定权都在父组件的实现上,任何一个组件对外的接口就是其数据+其事件,不会出现一个组件定义的事件有click,结果它的子组件的keypress从这个组件上触发了出来这种不符合接口定义的情况,保证了组件本身的静态类型特征
- 子组件对父组件是没有任何的感知的(不允许在代码中使用parent或parentComponent),这保证了整个结构是单向的,不会因为组件开发者双向的逻辑编写导致最终调用、事件流的混乱
- 没有任何隐式地穿透parentComponent的逻辑,事件和数据的传播均被设计为以组件为边界,拥有足够的封装性(Shadow DOM也是这一套逻辑)