组件结构决定通信方式:单向数据流
一般的组件分布是一个树的结构,这种结构决定了其基本的通信方式。
每个组件(也可以称之为节点)包含自身的数据和方法,暂统称为组件的状态State,props可以将父组件的state流向子组件,然后子组件通过props将自身的state流向孙组件,这是单向的数据流,是组件树中最基础的通信方式,因此不建议在子组件中通过修改props来更改父组件的state。如果这样做,父组件状态的修改的溯源是困难的,且会影响到所有的子组件,因此我们需要维护组件间的单向数据流。这是第一个共识。
谈到props,它的缺点显而易见,需要在每一个组件"停留",然后"传递"给下一个节点。(这里多说一句,在vue中存在$attrs和$listeners两个实例属性,它们看起来像是折中方式,$attrs和$listeners包含了父作用域中不作为prop被识别的属性和方法绑定,意味着可以在父孙组件间实现点对点通信)。这样麻烦而且容易造成代码冗余,在vue中,provide-inject 可以直接将状态从一个节点广播至所有的后代节点,而不需要做节点停留。它有很多的使用场景,例如主题切换,只需要在根节点上设置切换好的状态,那么所有inject到的主题变更都会进行。provide-inject 也是一种单向数据流,只不过它并不只是单纯的流向子节点。
如果说props是上下层节点的耦合是一种固化,那么provide-inject要显得“放松”了很多,它不需要刻意的维持这种上下层关系,祖先组件不需要知道哪些后代组件使用它provide状态,后代组件不需要知道被inject的状态来自哪里。然而,依赖注入还是有负面影响的。它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难(同样react的context也存在相同的问题)。
很多情况下,单向数据流并不能满足实际业务需求,树形结构也不是约束单向数据流的障碍,反而,我们可以通过树形结构去递归遍历整棵树分布每个节点的信息,vue也正是实现了这一点,从树形结构出发,遍历树节点获取节点上的状态是组件通信的另一种基础方式,$root表示树的根,$parent表示当前树的相对父节点,$children表示当前树的相对子节点们,如果想特定获取子节点,那么需要使用$ref进行额外的限制。常见的通过递归的方式(vue的1.0版本存在$dispatch和$broadcast两个api)可以方便的取到任意节点的任意状态。
面对一个庞大的树,如何快速的查找到节点,这需要算法的成本在里面。从上面提及到的结构耦合性上而言,不依赖于树的组织方式——可以在树上随意的遍历,但是,它仍有父子层级的局限。特别是在不同父节点下的兄弟组件之间的通信,是很困难,甚至有