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 odpowiedzi
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ł.
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>