【问题标题】:Vue 3 - Independent event propagation on slotsVue 3 - 插槽上的独立事件传播
【发布时间】:2021-12-29 09:14:29
【问题描述】:

今天我有一个关于 Vue 的棘手问题,涉及插槽和事件传播。 Here is a codesample

一些背景信息:我有一个订单,其中有 n 个订单位置。这些订单及其位置显示在不同的页面上。为了实现一致的 UI,我为 Order.vuePosition.vue 创建了 vue 组件。这些组件不包含任何业务逻辑,只负责在我的应用中保持一致的样式。

此外,我还有一个订单详细信息视图,它显示了我的订单信息及其仓位。

此外,我还有另一个视图,其中显示了相同的信息,并且应该可以选择一些订单/仓位进行取消。

数据包含在页面组件中,并作为道具传递给订单/位置。对于取消页面,我在插槽中传递了复选框组件:

// OrderCancellationPage.vue
<Order v-for="order in orders" :key="order.id" :id="order.id" :positions="order.positions">
  <InvoiceCancellationCheckbox :order-id="order.id" @isCancelled="onCancelledEvent($event)" />
</Order>
// Order.vue
<div>
  <h3>Order: # {{ id }}</h3>
  <slot></slot>
</div>
<OrderPosition
  v-for="position in positions"
  :key="position.posIndex"
  :pos-index="position.posIndex"
>
  <InvoiceCancellationCheckbox :orderId="id" :posIndex="position.posIndex"       @isCancelled="$emit('isCancelled', $event)"/> //propagate the event to order
</OrderPosition>
// OrderPosition.vue
<div>
  <span>Position {{ posIndex }}</span>
  <slot></slot>
</div>

我想实现,我的订单/头寸组件不包含任何取消逻辑。因此,InvoiceCancellationCheckbox 正在发出一个事件,如果它已被选中。 OrderPosition 正在使用 @isCancelled=... 监听此事件,该事件已在我的插槽组件中声明。最后,OrderCancellationPage 应该监听所有这些事件并存储所选订单及其位置。

问题:我收到此警告:

[Vue warn]: Extraneous non-emits event listeners (isCancelled) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option. 
  at <Order key="2021-9064-9333-9803" id="2021-9064-9333-9803" positions= 

如果我将emits: ['isCancelled'] 添加到Order,我可以解决此警告。但是,订单与取消复选框相结合,这是我不想要的。还有其他解决方案吗?

【问题讨论】:

  • 对于您的目的,您会认为自定义 v-model 是“非耦合”的吗?我正在使用可重用的组件,这些组件可能在没有严格上下文的情况下用于各种不同的地方,并且我正在使用自定义 v-model,它使用 prop modelValue 和一个通用的 emit emits: ['update:modelValue'] 将被发射在更改复选框时,每当我在父组件中需要它时,我都可以查看建模值
  • 感谢这工作到目前为止!相同的功能,没有此警告和更易于理解的代码。
  • 不客气。很高兴仅凭提示就可以帮助您找到合适的方法
  • 也许您想将解决方案发布为其他浏览此页面的人的答案
  • 我发布了我的解决方案,但它不适用于 Stackblitz,而是在我的本地机器上,

标签: vue.js vue-component


【解决方案1】:

编辑:似乎该示例不适用于 stackblitz,但它可以在本地计算机上运行。我在这段代码中没有看到任何错误...


v-model 保存了日期。

我用解决方案 fork 旧代码:https://stackblitz.com/edit/vue-t6ekts

基本上是关于在复选框中使用v-model

<template>
  <span>
    <input type="checkbox" :id="id" v-model="modelValue" />
    <label :for="id">Select for cancellation</label>
  </span>
</template>

<script>
  ...
  props: {
    modelValue: Boolean,
    orderId: String,
    posIndex: Number
  },
  emits: ['update:modelValue'],
  watch: {
    modelValue() {
      this.$emit('update:modelValue', this.modelValue);
    },
  },

并将其作为插槽传递:

<Order v-for="order in orders" :key="order.id" :id="order.id" :order="order">
  <CancellationCheckbox :order-id="order.id" v-model="order.isCancelled"/>
</Order>
<OrderPosition v-for="position in order.positions" :key="position.posIndex" :position="position">
  <CancellationCheckbox :orderId="order.id" :posIndex="position.posIndex" v-model="position.isCancelled"/>
</OrderPosition>

【讨论】:

  • 实际上网站上的失败给了你重要的提示。我forked your version。在您的 CancellationCheckbox 中,您也将 modelValue 用于 v-model。但是你应该避免改变一个道具,这可能会中断生产或由于继承的反应性而引入奇怪的交叉变化。我为 Checkbox 引入了一个新的数据点,并将其绑定到 prop modelValue(可以省略,因为它是 vue 默认的 emit/prop)。然后观察 boundValue 和 emit('update:modelValue') 的变化
  • 我认为忽略 modelValue 作为属性的声明是不正确的。使用 vue 开发者工具的检查器,我可以看到 modelValue 是一个属性而不是一个属性。在我的高级用例中(选择所有位置,如果选择了订单)我需要将其声明为属性,否则无法识别modelValue 上的更新。看看我的updated version
  • 在这种情况下,你是对的。
猜你喜欢
  • 2019-12-21
  • 2021-01-14
  • 2021-02-05
  • 2021-02-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-06
  • 2020-12-24
  • 2018-09-14
相关资源
最近更新 更多