【问题标题】:VueJS - Difference between $emit and $listenersVueJS - $emit 和 $listeners 之间的区别
【发布时间】:2020-06-18 14:39:45
【问题描述】:

让我们看下面的场景,其中WelcomePage(父)使用LoginForm(子)和自定义事件@submit

// WelcomePage.vue
<LoginForm @submit="handleLogin">Login<Button>

那么,组件LoginForm的代码如下:

// LoginForm
<form @submit.prevent="handleSubmit"> ... </form>
handleSubmit() {
  // do some stuff...

  // following Vue docs to create custom events.
  this.$emit('submit', 'some_data')

  // OR... we can also use $listeners.
  this.$listeners.submit('some_data')

  // do other stuf...
}

使用this.$listeners.submit() 代替this.$emit('submit') 有什么弊端吗?

使用this.$listeners 的一个优点是它可以与await 一起使用,is a limitation of $emit 迫使我们使用done() 回调方法。当我们想在自定义事件完成后更新某些状态(this.isLoading)时,它很有用。

将 $emit 与回调一起使用:

// LoginForm.vue
async handleSubmit() {
  this.isLoading = true

  this.$emit('submit', 'some_data', () => {
    this.isLoading = false
  })

}

// WelcomePage.vue
async handleLogin(data, done) {
  // await for stuff related to "data"...

  done();
}

将 $listener 与 await 一起使用:

// LoginForm.vue
async handleSubmit() {
  this.isLoading = true

  await this.$listeners.submit('some_data') // no need to use done callback

  this.isLoading = false
}

所以,我的问题是:可以使用this.$listeners 吗? this.$emit 的目的/优势是什么?


更新:

将 prop isLoading 从父级传递给子级将是第一个(明显的)选项,而不是使用 $emit

但这需要在每次我们使用子组件时在handleSubmit 上设置和更新this.isLoading = true | false(并且它被大量使用)。所以我正在寻找一种解决方案,当调用@submit 时,父母不需要担心isLoading

【问题讨论】:

  • 只是出于好奇.. 为什么要异步发射?
  • @SølveTornøe 一个具体的例子:LoginForm 需要显示“加载状态”,而WelcomePage 正在处理提交(异步)。完成后,LoginForm 需要清理加载状态。
  • 我认为在这种情况下通常的“goto”将是一个额外的发射loading。然后你在 async 函数之前发出 loading=true ,在之后发出 loading=false 。
  • 我误解了你的回答。您实际上想要做的是向LoginForm 提供加载道具。一旦父级获得了发射some_data,它会将加载设置为true,一旦完成就会返回false。

标签: javascript vue.js


【解决方案1】:

我认为$emit 可以帮助您保持 FLUX 架构。 您可以轻松查看数据流,它可以帮助您调试。

另一方面,使用$listeners 被认为是一种不好的做法。它可以完成,但它可能会破坏一种方式的数据流。 $root 也是如此,您仍然可以访问它,但这并不意味着您应该使用(修改)它;-)

不过,一如既往地使用什么取决于上下文和您的需要。请注意,一旦破坏了一种方式,数据流就很难调试。


评论后编辑:这只是我的观点。

当使用props$emit 作为组件间通信的推荐方式时。你有一个清晰的数据流。加上 Vue dev-tools 可以帮助您跟踪每个$emit,因此您可以一步一步确切地知道发生了什么。

当使用“collbackFunc”作为props 并在仍然可以工作的子组件中调用此回调时。这仍然是一个很好的方法。缺点是不推荐使用。

想象一下,您将“callbackFunc”传递给许多子组件。当出现问题时,很难追踪它是从哪里触发的。

同样适用于在$listeners 上直接调用方法。突然你的状态发生了变化,你不知道具体从哪里来。哪个组件以及何时触发它。

【讨论】:

  • 感谢您的回答!你能举个例子说明$listeners 可以在哪里打破流程吗?与$emit 相比,它的调试难度如何?
  • @sandrina-p 我已经更新了我的答案。但这只是我的观点;-)
  • @sandrina-p 如果您还想看到我昨天在聊天室中提到的动作的自动加载器,请告诉我。
  • 在项目中,我们决定保留$listeners的方式。这对我们的案例最有效,我们记录了它并解释了利弊,以帮助避免将来出现任何意外的重大变化。感谢您的帮助:)
【解决方案2】:

使用 props 而不是来自 $emit 的回调

const LoginForm = {
  name: 'Login-Form',
  props: {
    loading: {
      type: Boolean,
      default: false,
    }
  },
  template: `
    <button :disabled="loading" @click="$emit('some_data')">
      <template v-if="loading">Logging you in</template>
      <template v-else>login</template>
    </button>
  `,
};


new Vue({
  el: '#app',
  components: {
    LoginForm,
  },
  data() {
    return {
        loadingLoginForm: false,
    };
  },
  methods: {
    handleSubmit() {
      // Set loading state before async
      this.loadingLoginForm = true;
      
      // Some async shish
      setTimeout(() => this.loadingLoginForm = false, 1500)
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
  <login-Form :loading="loadingLoginForm" @some_data="handleSubmit" />
</div>

【讨论】:

    猜你喜欢
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 2017-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多