以下全是暴论

这是一个发生在今年的对话:

Q:组件通信有几种方式?
A:balabalabala(无非 props, provide/inject,vue 还有 $parent/$children
Q:还有吗?
A:postmessage 可以算吗
Q:也行,还有吗?
A:localStorage/sessionStorage 可以算吗?
Q:算半个,还有吗?

这 TM 不就这些方式还有吗?

回来一拉清单,各家文章盘点确实还多了一个:EventBus

什么是 eventbus

简单理解:事件总线,一种订阅/发布模型,即是在希望发生事件处理的地方创造一个监听器(listener),接着在希望影响到监听器所在位置变化或更新的地方加上发射器(emitter)

这样的好处是,任何位置都可以透传。因为事件总线(可以)是全局的

从名字上看,它应该是属于事件驱动设计的产物。这没问题,GUI 一直都跟事件驱动有关,浏览器在交互部分也是这么做的

那,这么多前端框架有没有使用事件驱动的?有,御三家里的 Angular。除了御三家那更多了,反而数据驱动才是新品种

OK 目的达成,我就是要绕到这个点上:为什么面试上问 Vue,甚至是 React,我就没想过 EventBus。因为这本来就跟数据驱动没关系

为什么我不用 eventbus

不带 Vue 玩了这领域有争议。就一个问题:什么时候 React 会触发更新?

这个时候就应该有个经典公式:view = f(props, state)。也就是当 props 或者 state 发生更新了,view 会更新。或者换句话说,数据更新了视图会更新

那这里就有一个问题:如果触发了某个事件,视图会更新吗?会更个几把! 要会更新你还需要 setState 吗?这也是为什么 rxjsReact 用起来就是没有 Angular 来得无尿点,最核心的一点就是你想通过 rxjs 通知视图更新就肯定需要在订阅做一次 setState,不管你封装了还是在 business code 里订阅一次写一次

而且我不用还有另一个原因:这东西在我的理解中,就是一个 goto —— 它会把代码弄得非常糟。本来是单向的数据流向,现在会完全不知道某个组件会因为什么而产生更新,流向变得不可观察

你可能会说,那 context 也是这么传的啊!可 eventbus 可不禁止子组件 call 父组件,不约定好就造成逆数据流

除此之外,事件名称是一个字符串(暂且只能是字符串),那么这个时候需要保证事件名不要重复。为了解决这个问题可能就会引入一个叫事件表的东西 —— 但你想用到某一个事件时,import 这个常量,毕竟常量背后什么静态值对于事件来说无所谓,对得上就行。这会发生什么问题?在生产过程中这问题发生得不要太多,最经典的就是忘记更新事件表,或者嫌麻烦摆烂直接写,最后发现因为一个大小写导致事件对不上……

除此之外还有!毕竟是一个订阅发布模型,你订阅了是不是得取消订阅。事件的东西你一旦开了他就会一直留存在内存中,处理不当的话可能会出现两个一模一样的 listener,而这个原因仅是因为上一个没有销毁……当然了最轻的问题就是你的某一个事件处理会响应 n 次,是的这只是最轻的

总之这非常反思维,在一个数据驱动的环境中,为了一点点 buff 而引入大量 debuff 是否得不偿失。经过一轮网上冲浪后发现,相关讨论不少(指两个)

你真的需要 eventbus?你用来做什么?

Vue 有 provider/inject,React 有 Context API,这应该够用了

跟数据有关的 API,Vue 有 Vuex,React 有 React / Recoil。他们本身也会遵循单向数据流

所以,到底是哪里需要在 React 环境中引入一个不属于数据驱动的东西。或者换个思路(挖个坑):

我们是否正在被框架绑定?一个本只是视图层框架,现在还包含了逻辑层?