Vue: vuejs2.0 应该保留 $broadcast, $dispatch 事件 api 吗? 2.0点对点亲子通信没有好的解决方案

创建于 2016-09-01  ·  41评论  ·  资料来源: vuejs/vue

@yyx990803
嗨,vuejs 核心团队,
我正在努力为父子组件与 vuejs2.0 之间的点对点通信找到一个好的解决方案。
因为 vuejs2.0 已经弃用了 $broadcast,$dispatch api,所以我很难找到 $broadcast,$dispatch 提供 vuejs2.0 事件总线功能的替代解决方案。
我已经在这个话题的论坛,写一个线程在这里,我想在这里贴了更多的讨论。

请不要关闭它,除非对 vuejs2.0 有好的想法或解决方案。

<pcom id=1>
    <soncoma></soncoma>
    <soncomb></soncomb>
</pcom>
<pcom id=2>
    <soncoma></soncoma>
    <soncomb></soncomb>
</pcom>

在上面的代码中,当 pcom1 的孩子,例如,soncoma $emit 一个事件,说,

bus.$emit('son-coma-event',somedata)

在 pcom 上,我们用

bus.$on('son-coma-event',function(){})

我只希望 pcom1 将处理该事件,但不幸的是 pcom2 也将处理该事件。
如何应对这种情况?

我的应用程序中的一种解决方法是使用 this.$parent 作为事件总线
在孩子:

this.$parent.$emit('some-event',someData)

在父级:

{
   created(){
       this.$on('some-event',function(){})
}

上述解决方法的缺点是:

  1. 我们在亲子关系上夫妻很多;
  2. 如果有更深层次的亲子层次就行不通了;

在一些更复杂的情况下,自定义事件系统会更难找到一个好的解决方案,例如递归组件

 <pcom>
    <recursivechild>
             <recursivechild>
                          <recursivechild>
                          </recursivechild>
             </recursivechild>
    <recursivechild>
</pcom>

 <pcom>
    <recursivechild>
             <recursivechild>
                          <recursivechild>
                          </recursivechild>
             </recursivechild>
    <recursivechild>
</pcom>

recursivechild 如何与其直接 pcom 组件通信?
请就这些主题提出您的想法或观点。
谢谢~!

discussion

最有用的评论

@rhyek我想就你提出的几点给出我的 2ct。 既然讨论已经刷了很多话题,我想回到基本的问题,为什么我们弃用了 $diospatch 和 $broadcast:

1.隐式耦合。

如果您有一个父级和一个深层嵌套的子级来调度事件,则无法从代码中推断出这种关系(显然,对于$broadcast也是如此。)

如果您查看我们在 Vue 2.0 中引入的其他更改和弃用,您可能会意识到删除隐式行为以支持显式替代方案是一个共同的主题,而弃用$dispatch正好适合其中。

例子:

// parent
events: {
  'some-event': function () { ... }
}

// deeply nested child:
$dispatch('some-event')

当父级只有一个直接子级时这很好 - 但在这种情况下,模板中带有侦听器的$emit()也不是真正的额外工作。

一旦嵌套了子级(尤其是嵌套很深),甚至不止一个直接子级,就很难跟踪(尤其是在团队中)——您要么必须查看所有子级,要么依靠代码注释来记录哪个事件是从哪个子组件触发 - 这也是额外的样板。

你说你喜欢 $dispatch 和 $broadcast 因为你不必通过其他组件传递它们。 而且我同意它更容易 - 但我们还没有遇到过很多情况,这实际上是必要的,或者更确切地说:如果有这样一个传递事件的链,那么数据会被更改/附加/在这次旅行中由中间的组件组成。

2. 事件名称

当您将$dispatch与深层嵌套的组件一起使用时,您必须非常明确地为事件命名空间,否则,它们可能会发生冲突:

// parent
events: {
  'close': function () { ... }
}

// deeply nested child 1:
$dispatch('close')
// deeply nested child 2:
$dispatch('close')

..如果那些孩子是第三方库,你现在就完蛋了。 或者必须在中间的某个组件中捕获事件,以便在$dispatch()进一步到父级之前重命名它。 并且不要忘记对此进行评论,因为查看此代码的人可能会想为什么除了重命名事件之外,您什么都不做。

使用 $emit 和模板侦听器没有这个问题。 您可以在任何地方使用简单、简短的事件名称,它们不会发生冲突,因为每个事件在模板@close="callback都有自己的回调。

我真的只是希望你没有拿走 * 选择 * 使用任何一种范式,

如果我们认为两种范式都可以同样有效,我们就会平等对待它们。 但我们不这么认为,出于上述原因以及更多原因。

因此,我们尝试引导用户采用我们发现最有效的策略,同时留出一种使用“全局总线”方法来绕过它的方法。

我也想谈谈你对全局状态的担忧,但我不确定我是否完全理解你的立场。

也许您可以提供一个您认为 $dispatch 和 $broadcast 最适合您的示例,我尝试向您展示“我们的”方法如何改善这种情况?

所有41条评论

因为这是一般性的提案讨论,我还没有创建小提琴。 如果需要,我想创建来演示。
谢谢~!

这已经在2.0 更改中得到了证明

这是一个副本:

如何处理$dispatch$broadcast弃用?

我们弃用$dispatch$broadcast是依赖于组件树结构的事件流很难推断组件树何时变大(简单地说:它不会在大型应用程序中很好地扩展,我们不想让你以后痛苦)。 $dispatch$broadcast也没有解决兄弟组件之间的通信。 相反,您可以使用类似于Node.js 中

var bus = new Vue()
// in component A's method
bus.$emit('id-selected', 1)
// in component B's created hook
bus.$on('id-selected', function (id) {
 // ...
})

在简单的场景中,这种模式可以替代$dispatch$broadcast 。 但是对于更复杂的情况,建议使用Vuex引入一个专用的状态管理层。

升级指南中显示的示例与您所说的相同

关于递归通信:多个组件可以监听同一个事件。 此事件由共同的父母识别,因此每个孩子都可以意识到它

@posva ,感谢您的信息。 总线集线器在简单的单组件点对单组件点通信中确实运行良好。 如果有多个组件的组件类型相同,则会出现问题,不幸的是,这是正常情况。 许多情况下,我想使用事件是更新属于某个特定组件的小数据。 当前的事件总线实现并没有给出目的地或始发节点的信息(我见过每个组件的 _uid,也许我们可以使用这个唯一的 _uid 进行事件连接?),所以 vuejs2.0 实际上不能支持点对点事件通信. 确切地说,vuejs2.0 事件总线只支持组件类型到组件类型的通信? 是否有一个简单的解决方案来满足该要求:由树中的事件触发并更新其自己的数据
vuex 非常适合应用程序级全局静态数据管理,但据我了解,它可能不适用于特定的本地组件数据管理?
欢迎对此问题进行更多思考。

@cnweibo我已经在论坛上用一个例子回答了你的问题。 我认为这个例子将满足您的需求。
http://forum.vuejs.org/topic/4832/vue2-0-event-bus-issue-how-to-deliver-event-to-parent-when-the-same-multiple-parent-children-in- dom/6

是否有一个简单的解决方案来满足该要求:由
树中的事件并更新它自己的数据

这可以通过简单的 vuex 解决,没有复杂的事件系统。

vuex 非常适合应用级全局静态数据管理,
但据我所知,它可能不适用于特定的本地组件数据
管理

“本地”仅表示单个组件。 您的用例正在管理状态
多个组件,所以它是全球性的。
并且 vuex 可以有模块(状态子树),所以它不是“全局变量”
有点“全球性”。 您可以使用自己的 vuex 驱动组件组
模块。

整个“事件与共享状态”的讨论已经解决了几个月
之前,结论是要么使用全局事件总线,要么使用 vuex。 所以我会
建议您阅读有关 vuex 及其工作原理的更多信息。

在星期四,2016年9月1日,16:17 cnweibo [email protected]写道:

@posva https://github.com/posva ,感谢您的信息。 公交车
集线器在简单的单组件点对单组件中确实运行良好
点沟通。 如果有多个组件具有相同的组件
类型,会有问题,不幸的是,这是正常情况。 许多
情况下,我想使用事件是更新属于某些的小数据
特定组件。 当前的事件总线实现没有给出
关于目的地或始发节点的信息(我已经看到每个
组件,也许我们可以使用这个唯一的 _uid 进行事件连接? ), 所以
vuejs2.0 实际上不能支持点对点事件通信。 成为
确切地说,vuejs2.0 事件总线只支持组件类型到组件类型
沟通? 是否有一个简单的解决方案来满足该要求:
由树中的事件触发并更新_ITS OWN DATA_
vuex 非常适合应用级全局静态数据管理,
但据我了解,它可能不适用于特定的本地组件数据
管理?

欢迎对此问题进行更多思考。


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/vuejs/vue/issues/3581#issuecomment -244008699,或者静音
线程
https://github.com/notifications/unsubscribe-auth/AFTLl1bjHqWTVDAkr8Fqbx0WiTuH16n2ks5qlooBgaJpZM4JyUfJ
.

我现在将关闭这个问题,因为

  1. 提供了解决方案并
  2. Github 问题无论如何都不是寻求支持的正确地方(请参阅指南)

@ktsn ,感谢您的演示小提琴。 这正是我想要的简单解决方案!
@fnlctrl ,我会花更多时间在所谓的模块化 vuex 状态管理上

再次感谢~!

在我看来,不需要 $broadcast,只需要下降道具数据流。
任何 $dispatch 都可以像这样(在 Vue 1 中)重新实现为单级 v-on 和 $emit:
每个涉及的组件上的<child-component @select="$emit('select', $arguments[0])" />
在任何其他情况下都应该使用自定义事件总线

实际上,组件本身的自定义事件的组件标签上的 v-on 也可以与组件本身的 $emit 一起使用。

@cnweibo为记录,可检索事件数据。

{
  template: `<foo @bar="dosomething">`,
  methods: {
    dosomething(params1, params2, ... and all the event data args) {}
  }
}

但是在vuejs2.0中无法检索到事件数据

当然可以,是什么让你不这么认为?

@fnlctrl
@LinusBorg
对不起,我有误解,把事情说不清楚。 在提供的模式@fnlctrl 中,确实可以检索其事件处理程序中的所有数据,即使使用

 <comp @some-event-emitted-by-comp-internal-template="someFuncInParentScope"></comp>

从另一个问题复制/粘贴我自己的评论: https :

我认为删除 $dispatch 是一个糟糕的主意。 这不是第一个实现在可视树上冒泡动作/事件概念的 ui 框架/库。 这是一个成熟的想法。 为什么要在“能够调度一个在其未知父树中引起副作用的事件听起来像是给我带来麻烦的方法”的前提下取消此功能? 您需要将此责任留给用户。 我相信大多数人都有正确使用此功能的常识。 这不是一个新概念!

当这个库建立在长期建立的网络技术/概念上时,我真的看不到这种变化的好处,比如 DOM 和 DOM 事件,它们实际上使可视化树冒泡,并且多年来一直没有人这样做抱怨。 由于 W3C 对 Web 组件的提议,组件的想法不是最近被接受的吗? 在我看来,只有在事件处理的完成方式方面,Vue 组件的行为与常规 DOM 元素的行为相似才有意义。

当更实用、更有效和更容易理解的东西已经存在时(因为它是一个成熟的概念多年),使用全局事件总线的提议替代方案对我来说似乎不合逻辑。

该线程中的其他建议让我想起了 EmberJS 想要如何做到这一点。 将闭包操作作为属性传递给层次结构中每个级别的组件。 如此乏味和不必要! Vuejs 是我写https://www.npmjs.com/package/ember-component-action-bubbling的原因

除此之外,我真的很喜欢你的图书馆。 但说真的,我认为这是一个可怕的变化。

事件总线范式只是作为一种解决方案来做完全相同的事情,当基于事件的架构通常不如声明性的、基于状态的架构时。

假设您有一个用户可以登录的应用程序。使用基于事件的解决方案:

  1. 监听登录事件
  2. 稍后使用用户名和密码触发事件
  3. 在任何正在侦听的组件中相应地响应所述事件

但是,这带来了一些问题。 最大的问题是,在触发事件时未在 DOM 中呈现的组件不会接收更改,而且您根本不知道应用程序的哪些部分将接收事件。 此外,如果不提供额外信息,事件的接收者不可能知道事件来自哪里。 请原谅我的语言,但这是一个我已经处理过的大规模集群并且不想再次使用。

所以让我们使用有状态的方法:

  1. 创建一个表示用户帐户信息的全局状态变量。 退出时null ,登录时为用户登录信息。
  2. 当用户登录时,设置帐户信息。

应用程序中依赖此状态变量的所有内容都会相应更新。 没有事件,创建组件的时间和地点无关紧要,因为它始终会显示正确的信息。 您可以使用事件来更新全局状态,但是为什么要在您可以……更新它的情况下这样做呢?

声明式方法允许您以完全相同的方式编写组件,无论用户在您的应用程序中做什么,都完全取决于本地/全局状态,而无需监听发生的事情。 我相信这就是 Vue 一直以来的意义,但类似于软件开发中的大多数事情,我们花了一段时间才弄明白。 但我很高兴我们做到了。

编辑:哦,不要忘记您可以监视参数,例如发送 AJAX 请求,或在更改时执行其他操作。 例如,在用户登录后,观察 'loggedIn' 变量,当它为真时,加载他们的个人资料图片或类似的东西。

我明白你在说什么,但是我认为让一些随机组件修改全局状态与让该随机组件将事件冒泡到上帝知道哪里是一回事。 您仍然面临着相同的风险,即意外“弄脏”应用程序的流程。

有办法以干净的方式处理这两种范式,这通常(并且应该)最终成为框架用户的责任。

在某些情况下,一种机制比另一种机制更有意义。 例如,我同意登录状态应该是整个应用程序都知道的:全局状态是有意义的。 但是应用程序用户单击的按钮通常不会处理实际登录用户背后的逻辑。这将是在链上层处理的事情。 它可能是一个组件,可能是一条路线。 该按钮可能只需要通知某些登录意图。因此,该按钮无需直接修改您的全局状态。

所以现在,随着 $dispatch 的删除,您需要按钮组件了解一些管理应用程序用户会话的全局对象并直接通知它意图。 这使得按钮与您的整个应用程序紧密耦合。

或者,您可能将按钮嵌套 10 层深,并且您必须在每一层都声明一个v-on:login处理程序,以便意图达到其目标。 完全没有必要。

实际上,必须在每个级别都执行v-on只会使您的代码更难维护。

很明显,仅仅直接改变状态可能会有问题,但 Vuex 解决了这个问题的突变和动作。 确实,某些解决方案比其他解决方案更适合该法案,但我从未遇到过声明性逻辑不是更好选择的情况。

在您的具体情况下,我可能不会制作特定于登录的按钮,呵呵。 另外,如果一个登录相关的组件由于某种原因嵌套那么深,只需让它改变全局状态。

登录按钮只是一个例子。 当我提到“某个全局对象”时,我指的是 vuex 商店。 我必须研究 vuex 的工作原理,但我希望您只需管理对商店的引用,就能将一些随机组件与应用程序的其余部分紧密耦合。

例如,如果登录按钮是某个第三方库的一部分,则这是不可取的。

我真的只是希望你不要拿走 *_choice *_ 来使用任何一种范式,尤其是当事件冒泡被广泛认可并且因此对于项目的新贡献者来说很容易理解时。

这取决于您所说的“一些随机组件”是什么意思,因为例如,路由器视图组件在我看来具有访问和提交到商店的完全权限。 但是,如果它是一个较小的组件以供重用,例如按钮、表单或其他一些 UI 元素,10 次中有 9 次,则不应该有任何逻辑理由让它访问商店,而不是使用道具和事件。

由于 Vue 应用程序中的数据是自顶向下的,因此您希望尽可能多地将本地状态保持在顶层。 深度嵌套本身就是一个需要尽可能避免的问题。 将事件向下传播两个级别并不是_那_ 多麻烦,但是如果它比这更深,您可能需要重新考虑模板结构。

不过,这主要是一个切线。 很多时候,更容易理解的范式最终会被滥用到地狱的尽头并变得笨拙。 正如我们大多数目前使用 2.0 的人所认同的那样,基于状态的方法要简单得多。 如果这种方法不是您的偏好,您可以继续使用 1.0,或者转移到另一个框架。

10 次中有 9 次,它不应该有任何合乎逻辑的理由访问商店,而不是使用道具和事件。

正是我的观点。

深嵌套本身就是一个要尽量避免的问题

有时,这不是一种选择。

向下传播两个级别的事件并没有那么麻烦

当这些级别更深时,情况就不那么正确了。

更容易理解的范式是那些最终被滥用到地狱尽头并变得笨拙的范式

这应该取决于您的用户的纪律。

如果这种方法不是您的偏好,您可以继续使用 1.0,或者转移到另一个框架。

嗯。

简单和方便是我考虑从 Ember 切换到 Vue 的原因。 $dispatch是我喜欢 Vue 的事情之一,删除它对我来说似乎太随意了。

该团队为 2.0 版本删除了许多功能。 老实说,我同意所有这些。 只是不是这个。

谢谢您的回复。

@ktsn $broadcast 和 $dispatch 的替代方案非常简单,这个移除已经改进并变得更好。

@rhyek我想就你提出的几点给出我的 2ct。 既然讨论已经刷了很多话题,我想回到基本的问题,为什么我们弃用了 $diospatch 和 $broadcast:

1.隐式耦合。

如果您有一个父级和一个深层嵌套的子级来调度事件,则无法从代码中推断出这种关系(显然,对于$broadcast也是如此。)

如果您查看我们在 Vue 2.0 中引入的其他更改和弃用,您可能会意识到删除隐式行为以支持显式替代方案是一个共同的主题,而弃用$dispatch正好适合其中。

例子:

// parent
events: {
  'some-event': function () { ... }
}

// deeply nested child:
$dispatch('some-event')

当父级只有一个直接子级时这很好 - 但在这种情况下,模板中带有侦听器的$emit()也不是真正的额外工作。

一旦嵌套了子级(尤其是嵌套很深),甚至不止一个直接子级,就很难跟踪(尤其是在团队中)——您要么必须查看所有子级,要么依靠代码注释来记录哪个事件是从哪个子组件触发 - 这也是额外的样板。

你说你喜欢 $dispatch 和 $broadcast 因为你不必通过其他组件传递它们。 而且我同意它更容易 - 但我们还没有遇到过很多情况,这实际上是必要的,或者更确切地说:如果有这样一个传递事件的链,那么数据会被更改/附加/在这次旅行中由中间的组件组成。

2. 事件名称

当您将$dispatch与深层嵌套的组件一起使用时,您必须非常明确地为事件命名空间,否则,它们可能会发生冲突:

// parent
events: {
  'close': function () { ... }
}

// deeply nested child 1:
$dispatch('close')
// deeply nested child 2:
$dispatch('close')

..如果那些孩子是第三方库,你现在就完蛋了。 或者必须在中间的某个组件中捕获事件,以便在$dispatch()进一步到父级之前重命名它。 并且不要忘记对此进行评论,因为查看此代码的人可能会想为什么除了重命名事件之外,您什么都不做。

使用 $emit 和模板侦听器没有这个问题。 您可以在任何地方使用简单、简短的事件名称,它们不会发生冲突,因为每个事件在模板@close="callback都有自己的回调。

我真的只是希望你没有拿走 * 选择 * 使用任何一种范式,

如果我们认为两种范式都可以同样有效,我们就会平等对待它们。 但我们不这么认为,出于上述原因以及更多原因。

因此,我们尝试引导用户采用我们发现最有效的策略,同时留出一种使用“全局总线”方法来绕过它的方法。

我也想谈谈你对全局状态的担忧,但我不确定我是否完全理解你的立场。

也许您可以提供一个您认为 $dispatch 和 $broadcast 最适合您的示例,我尝试向您展示“我们的”方法如何改善这种情况?

我认为 $dispatch/$broadcast 和事件总线处理不同的事情。 它们可以使代码在不同场景下易于维护和解耦。 如果我们能把它们都保留下来,那就太好了。
很难说在每种情况下一个都比另一个好。

@cnweibo我认为双方都有非常详尽的论点,老实说,我不明白你关于“解决不同的事情”的观点。 随意提出进一步的论点,但我可以肯定地告诉你这不会发生。

如果您真的很想要它,那么将它自己作为插件来实现并不难。

@LinusBorg我真的很感谢你花时间写你的回复,我理解你的论点。

你说你喜欢 $dispatch 和 $broadcast

老实说,我只是喜欢$dispatch$broadcast对我来说肯定很奇怪。 就像我说的, $dispatch只是事件冒泡,在这一点上,它在许多平台中无处不在。 $broadcast ... 没那么多。 我遇到过的唯一类似的事情是 WPF 中与正常事件配对的“预览”事件。 它们将视觉树从最顶部的元素“隧道”到原始事件的源,但它们只会直接向下发送到相关元素链,而不会_传播_到所有内容。

你必须非常明确地命名你的事件

我同意这一点,无论如何,这通常是我所做的。 这也是人们习惯于在 jQuery 上做的事情。 此外,某些平台只是将“源”对象作为参数发送给处理程序,您可以基于此过滤上下文(提示: instanceof )。 例如,DOM 事件有event.target可用。 其他平台具有使用静态类型的好处,因此很难遇到这种“冲突”(事件是类的实例)。

无论如何,老实说,我不明白为什么 VueJS 团队会如此关注这一点。 如果人们在使用$dispatch不够小心,我相信你会发现他们在使用你的库时做错了很多其他事情。 您将在多大程度上“保护”您的用户免于粗心?

当父级只有一个直接子级时这很好 - 但在这种情况下,模板中带有侦听器的 $emit() 也不是真正的额外工作。

诚实的问题(因为我绝对是 Vue 的新手),除了在每个级别声明侦听器之外,您是否还必须在链上的每个组件上$emit事件? 这看起来烦人。

最后,让我引用某人在vue-cli 上关于为新项目引入“官方模板”的问题所说的话(https://github.com/vuejs/vue-cli/issues/123#issuecomment-233071630):

正如您可能知道的那样,您并没有局限于官方模板。 这给了你自由,但同时也导致你需要自己做更多的决定。

我认为最后只是一米的平衡。 我们可以为所有(大多数)用户预先做出多少决定,以及用户想要自己做多少或哪些决定。

我同意这种哲学,但奇怪的是,这与我在_this_ 问题上遇到的态度不同。 我看到的那个评论和这里的评论之间的对比归结为自由。 您正在根据尽管是好的但最终有缺陷的意图来取消选择。

如果您真的很想要它,那么将它自己作为插件来实现并不难。

@yyx990803这可能就是

@LinusBorg还有:

因此,我们尝试引导用户采用我们发现最有效的策略,同时留出一种使用“全局总线”方法来绕过它的方法。

你不是“试图引导”,你是在强迫。 :)

您是否尝试使用dispatch和 EventBus 方法实现相同的功能?
它可能有帮助

@posva我打算这样做,但我的意思是,您基本上在某个模块上声明了事件总线对象,然后将其导入到$emit任何其他位置,不是吗? 我不喜欢那样,tbh。 我肯定会在某些事情上使用它,但我坚信这不是我每次都想做的事情。

无论如何,老实说,我不明白为什么 VueJS 团队会如此关注这一点。 如果人们在使用$dispatch不够小心,

好吧,关键是团队中没有人可以指出$dispatch()的实际用例,其中它比其他解决方案更可取(不仅$emit() ,而且还包括公共汽车,或global state),而且我们在无数的论坛帖子中也没有看到我们回答的任何内容。

这可能是一个主观的看法,但也许你可以理解,如果整个团队认为“根据我们的经验,这始终是一个较差的解决方案”,我们就从核心将其丢弃。

在这一点上,我想更新我的提议来讨论一个真实的例子。

我敢肯定,您可以使用您的图书馆找到很多其他他们做错的事情。 您将在多大程度上“保护”您的用户免于粗心?

这当然是一个微妙的问题,而且很难找到平衡点。 我们将不得不逐案判断。

当父级只有一个直接子级时这很好 - 但在这种情况下,模板中带有侦听器的 $emit() 也不是真正的额外工作。

诚实的问题(因为我绝对是 Vue 的新手),除了在每个级别声明侦听器之外,您是否还必须在链上的每个组件上 $emit 事件? 这看起来很烦人。

由于我谈论的是直接的父子关系,因此您只需 $emit() 一次。

如果你有很深的嵌套子级,当然你必须在每个级别重新发送,但重复我自己:我们没有发现也没有出现过在许多嵌套子级之间调度的情况真的是必要的或比其他解决方案更可取。

因此,我们尝试引导用户采用我们发现最有效的策略,同时留出一种使用“全局总线”方法来绕过它的方法。

你不是“试图引导”,你是在强迫。 :)

我想说我们让你的生活变得更艰难——你可以

  • 使用总线,这不是一回事,但在大多数情况下可以实现类似的行为。
  • 很容易地将其重新实现为插件。

我们不会强迫您使用$emit() ,我们只是让您更难解决它。

我认为您也可以将其添加到每个 Vue 实例中: Vue.prototype.$bus = new Vue()
不喜欢某事不是很有建设性...
我会等你的例子😄

@posva

我认为你也可以将它添加到每个 Vue 实例中: Vue.prototype.$bus = new Vue()

我喜欢。 很聪明。

不喜欢某事不是很有建设性

我认为这是选择使用与否的一个非常有效的标准,至少对我来说是这样。 无论如何,我已经多次说明为什么我认为事件冒泡有其地位。

我会等你的例子😄

我的意思是,我真的需要吗? 我已经举例说明了我不喜欢在每个级别使用事件总线或声明侦听器的想法。 你想看代码? 我也许可以想出一些东西来使我的观点更清楚一点,但我觉得事件冒泡是一个非常标准的东西,大多数人都认为这是有用的东西,而事件总线或状态管理器,至少对我来说,是一种范式转变,虽然不难理解,但似乎是时髦的领域。 😄

我当然是在开玩笑最后的评论。 就像我之前所说的那样,我确实看到了它们的用途,并且肯定会找到与它们一起解决的问题。 实际上,在我使用 Ember 进行的一些项目中,我倾向于编写一个“服务”,它的行为与全局状态管理器完全一样。 我向你保证,我不是故意对这件事固执己见。

我真的很喜欢 Vue。 我只是想_爱_它,你知道吗?

您是否还必须在每个组件上 $emit 事件

如果您从事件冒泡的角度考虑,您会这样做。 如果您使用组件组合,则不会。

例如:

<div>
  <a-button @click="modalShown = true">Open modal</a-button>
  <a-modal v-if="modalShown">
    <a-button @click="modalShown = false">Close modal</a-button>
  </a-modal>
</div>

a-modal组件不需要关心a-button组件的事件,因为a-modal和两个a-button实例都是单个实例的“直接逻辑孩子” ,尽管具有复杂的嵌套视图层次结构。

我想我现在只是在重复自己。 我最终会以某种方式解决这个问题。 我觉得奇怪的是,这个讨论围绕着一些非常标准和实用的解决方法展开,但无论出于何种原因都不再存在。

@LinusBorg

好吧,关键是团队中没有人可以指出 $dispatch() 在现实生活中比其他解决方案更可取的用例(不仅是 $emit()

在我看来,这听起来像是一位土木工程师问我不关闭高速公路出口的理由:

他说了类似的话,“这个出口匝道通向一个十字路口,人们在那里对是左转还是右转感到困惑。大多数人都知道这条路,因为他们在这里住了多年,但新公民经常迷路。分钟,我们希望避免这种情况。”

我说,“好吧,当然,你可以关闭它,这很酷。我只需要再行驶 10 公里到下一个出口匝道。我会处理的。”

:) 谢谢大家的回复。 我很高兴你们都愿意讨论,至少。 看起来是个不错的团队。

在我看来,这听起来像是一位土木工程师问我不关闭高速公路出口的理由:

他说了类似的话,“这个出口匝道通向一个十字路口,人们在那里对是左转还是右转感到困惑。大多数人都知道这条路,因为他们在这里住了多年,但新公民经常迷路。分钟,我们希望避免这种情况。”

我也会加上我的坏比喻。 :) 在我看来,它更像是:

  • “这条路鼓励人们比他们应该跑得更快。很少有人注意限速。我们应该加一些保险杠,迫使人们减速。”
  • “但我喜欢通过超速快速到达目的地!”
  • “当然,它可能会为您服务好几次,但我们一直观察这部分路超过 1.5 年,事故和错过下一个出口匝道的人数根本不值得,因为许多司机这些问题之一已经证明了这一点。”
  • “但我不喜欢那些保险杠!”
  • 嗯,对于 99% 的目的地,另一条路与保险杠之前的这条路一样快,就走这条路。
  • “但我不_喜欢_这条另一条路!景色不是那么好!”
  • ...

不喜欢某事不是很有建设性

我认为这是选择使用与否的一个非常有效的标准,至少对我来说是这样。 无论如何,我已经多次说明为什么我认为事件冒泡有其地位。

我认为@posva 的意思是:在与广泛的用户讨论是否保留某些内容或向库中添加某些内容时,“我喜欢这个”并不是建设性的论点。 这就是为什么我一直要求讨论一个有效的、现实生活中的用例,而不是个人偏好。

是的,除了你炸毁了完美的高速公路并用橡胶沥青建造了另一条因为你在某个地方读到它非常整洁。 此外,这是一个额外的10公里。 :)

关于这个例子:我认为关于递归组件的原始问题上发布的那个是一个不错的。 试想一下,当一个事件被捕获并且你有一个非常好的例子时,你想在每个级别按顺序做一些事情。 用 $dispatch 做到这一点非常简单。

你想看代码?

是的,请

@posva我在前面的评论中举了一个例子。 注意:如果你不得不在没有 $dispatch 的情况下多花几秒钟思考如何做到这一点,这证明了我的观点。

关于这个例子:我认为关于递归组件的原始问题上发布的那个是一个不错的。 试想一下,当一个事件被捕获并且你有一个非常好的例子时,你想在每个级别按顺序做一些事情。 用 $dispatch 做到这一点非常简单。

$dispatch()

// recursive-child
<template>
  <recursive-child></recursive-child>
  <button @click="dispatch">Do something</button>
<template>

<script>
  export default{
    methods: {
      dispatch() { this.$dispatch('do-something') }
    },
    events: {
      'do-something': function () { 
         // do something, or don't
         return true // nessessary to make the event bubble up further. Don't like the un-expressivness of this
       }
    }
  }
</script>

$emit()

// recursive-child
<template>
  <recursive-child @do-something="doSomething"></recursive-child>
  <button @click="doSometing">Do something</button>
<template>

<script>
  export default{
    methods: {
      doSomething() {
        // do someting, or don't
        this.$emit('do-something')
      }
    }
  }
</script>

那真的更糟吗?

有另一个吗? :)

可以,当然。 如果它们不是递归的,但仍然像这样嵌套怎么办?

好吧。 现在是凌晨 5 点,多亏了你,我将度过艰难的一天。 如果我以后想出更好的东西,我会发布它,如果没有,那么,要么你赢了,要么我失去了兴趣:)

如果它们不是递归的,但仍然像这样嵌套怎么办?

您必须在每个模板中为$emit()添加一个@event=侦听器,而对于$dispatch()不是这样,仅此而已。

这可以看出是好是坏,这取决于您是否强调冗长与表现力。

顺便说一句。 如果在某种情况下您必须将事件链接到父级,则可以执行以下操作:

<child-comp @event="$emit('event', $arguments)>
  • 您会为组件之间的这种“耦合”感到惋惜。
  • 我会赞美表现力
  • 除此之外,打字有点多,但没什么大不了的。
  • 我仍然声称,通过一个真实的用例示例,可能有不同的优化可用,例如全局存储 - 但这取决于个别场景,并且很难与示例代码争论。

无论如何,愉快的讨论,享受你当之无愧的睡眠。

@rhyek它看起来像变通方法只是因为您首先决定使用$dispatch ,但这不是目标。 目标是允许组件以良好的可维护性相互通信。 在实现该目标时,当您列出所有实际利弊(不包括偏好)时, $dispatch是次等解决方案,因此我们放弃了它。

还要注意 DOM 事件冒泡与跨组件通信有着根本的不同。 “事件冒泡被广泛认可”的论点并不意味着它一定是我们试图解决的问题的一个很好的解决方案。

我已经在这个线程中发表了评论,因为我发现很难与“我只是不喜欢它”争论。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

bdedardel picture bdedardel  ·  3评论

wufeng87 picture wufeng87  ·  3评论

julianxhokaxhiu picture julianxhokaxhiu  ·  3评论

gkiely picture gkiely  ·  3评论

aviggngyv picture aviggngyv  ·  3评论