Weźmy następujący scenariusz, w którym WelcomePage (rodzic) używa LoginForm (dzieci) ze zdarzeniem niestandardowym @submit:

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

Następnie komponent LoginForm ma następujący kod:

// 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...
}

Czy jest jakaś wada używania this.$listeners.submit() zamiast this.$emit('submit')?

Jedną z zalet korzystania z this.$listeners jest to, że można go używać z await, które to ograniczenie $ emit, które zmusza nas do użycia metody wywołania zwrotnego done(). Jest to przydatne, gdy chcemy zaktualizować jakiś stan (this.isLoading) po zakończeniu zdarzenia niestandardowego.

Używanie $ emit z wywołaniem zwrotnym:

// 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();
}

Używanie $ Listeners z await:

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

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

  this.isLoading = false
}

Zatem moje pytanie brzmi: czy można używać this.$listeners? Jaki jest cel / zaleta this.$emit?


AKTUALIZACJA:

Przekazanie właściwości isLoading od rodzica do dzieci byłoby pierwszą (oczywistą) opcją zamiast używania $emit.

Ale to wymagałoby ustawienia i aktualizacji this.isLoading = true | false na handleSubmit za każdym razem, gdy używamy komponentu potomnego (i jest on często używany). Dlatego szukam rozwiązania, w którym rodzic nie musi się martwić isLoading, kiedy @submit zostanie wezwany.

2
sandrina-p 6 marzec 2020, 00:33

2 odpowiedzi

Najlepsza odpowiedź

Według mnie $emit pomaga zachować architekturę FLUX. Możesz łatwo zobaczyć przepływ danych, pomaga to w debugowaniu.

Z drugiej strony używanie $listeners jest uważane za złą praktykę. Można to zrobić, ale może zakłócić jednokierunkowy przepływ danych. To samo dotyczy $root, nadal masz do niego dostęp, ale to nie znaczy, że powinieneś go używać (modyfikować) ;-)

Jednak to, czego użyć, jak zawsze, zależy od kontekstu i Twoich potrzeb. Tylko uważaj, raz uszkodzony jednokierunkowy przepływ danych jest bardzo trudny do debugowania.


Edytuj po komentarzu : to tylko mój punkt widzenia.

Podczas używania props i $emit jako zalecanego sposobu komunikacji między komponentami. Masz przejrzysty przepływ danych. Narzędzia deweloperskie Plus Vue pomagają śledzić każdy $emit, dzięki czemu dokładnie wiesz, co się dzieje, krok po kroku.

Używając „collbackFunc” jako props i wywołuj to wywołanie zwrotne w komponencie podrzędnym, który nadal będzie działał. I nadal jest to dobra droga. Wadą tego jest to, że nie jest to zalecane użycie.

Wyobraź sobie, że przekazujesz tę funkcję zwrotną do wielu komponentów potomnych. Gdy coś idzie nie tak, bardzo trudno jest wyśledzić miejsce wystrzelenia.

To samo dotyczy bezpośredniego wywoływania metody na $listeners. Nagle twój stan się zmienia i nie wiesz dokładnie, skąd. Który i kiedy składnik go odpalił.

1
Adam Orlov 6 marzec 2020, 09:08

Używanie właściwości zamiast wywołań zwrotnych z $ 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>
0
Sølve Tornøe 5 marzec 2020, 22:28