Jak przekazywać dane między komponentami w Vue.js

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Przy tak wielu różnych sposobach udostępniania danych między komponentami powinieneś wiedzieć, która technika jest najlepsza w Twojej sytuacji. Przeanalizujmy trzy najczęstsze sposoby przekazywania danych w VueJS.

Udostępnianie danych między komponentami jest jedną z podstawowych funkcji VueJS. Umożliwia zaprojektowanie bardziej modułowego projektu, kontrolowanie zakresów danych i tworzenie naturalnego przepływu danych w aplikacji.

O ile nie tworzysz całej aplikacji Vue w jednym komponencie (co nie miałoby sensu), napotkasz sytuacje, w których będziesz musiał udostępniać dane między komponentami.

Pod koniec tego samouczka poznasz trzy sposoby, aby to zrobić.

  • Używanie rekwizytów do udostępniania danych od rodzica do dziecka,
  • Emisja niestandardowych zdarzeń w celu udostępniania danych od dziecka do rodzica,
  • Używanie Vuex do tworzenia stanu współdzielonego na poziomie aplikacji.

Dobra — przejdźmy od razu!

Tworzenie aplikacji za pomocą Nuxt

Dzięki Spotify Twoi znajomi mogą sprawdzić, do czego jammujesz. Co by było, gdyby reszta Internetu również mogła doświadczyć twojego algorytmu? Dowiedz się, jak skomponować własną aplikację, aby udostępniać to, czego słuchasz w Spotify, korzystając z Vue.js i Nuxt. Przeczytaj powiązany artykuł →

Więcej po skoku! Kontynuuj czytanie poniżej ↓

1. Używanie rekwizytów do udostępniania danych od rodzica do dziecka

Rekwizyty VueJS to najprostszy sposób udostępniania danych między komponentami. Rekwizyty to niestandardowe atrybuty, które możemy nadać komponentowi. Następnie w naszym szablonie możemy nadać wartości tych atrybutów i — BAM — przekazujemy dane od rodzica do komponentu podrzędnego!

Załóżmy na przykład, że pracujemy nad stroną profilu użytkownika i chcemy, aby komponent potomny akceptował właściwość nazwy użytkownika. Potrzebujemy dwóch składników.

  1. Komponent potomny akceptujący właściwość, nazwijmy to AccountInfo.vue .
  2. Komponent nadrzędny przekazujący właściwość, nazwijmy to ProfilePage.vue .

Wewnątrz AccountInfo.vue możemy zadeklarować akceptowane przez nią rekwizyty za pomocą opcji rekwizyty. Tak więc w opcjach komponentów sprawmy, aby wyglądało to następująco.

 // AccountInfo.vue <template> <div id='account-info'> {{username}} </div> </template> <script> export default { props: ['username'] } </script>

Następnie, aby faktycznie przekazać dane z elementu nadrzędnego ( ProfilePage.vue ), przekazujemy je jak atrybut niestandardowy.

 // ProfilePage.vue <account-info username='matt' />

Teraz, gdy załadujemy naszą stronę, widzimy, że nasz komponent AccountInfo poprawnie renderuje wartość przekazaną przez swojego rodzica.

Podobnie jak podczas pracy z innymi dyrektywami VueJS, możemy użyć v-bind do dynamicznego przekazywania właściwości. Na przykład, powiedzmy, że chcemy ustawić właściwość nazwy użytkownika, aby była równa zmiennej. Możemy to osiągnąć, używając skrótu dla dyrektywy v-bind (lub po prostu : w skrócie). Kod wyglądałby trochę tak:

 <template> <div> <account-info :username="user.username" /> </div> </template> <script> import AccountInfo from "@/components/AccountInfo.vue"; export default { components: { AccountInfo }, data() { return { user: { username: 'matt' } } } } </script>

Oznacza to, że możemy zmienić nasze dane i sprawić, że wszystkie właściwości podrzędne korzystające z tej wartości również zostaną zaktualizowane.

Wskazówka: zawsze sprawdzaj swoje rekwizyty

Jeśli chcesz napisać jaśniejszy kod Vue, ważną techniką jest weryfikacja swoich właściwości. W skrócie oznacza to, że musisz określić wymagania dla swojej właściwości (tj. typ, format itd.). Jeśli jedno z tych wymagań nie zostanie spełnione (np. jeśli do właściwości zostanie przekazany niepoprawny typ), Vue wydrukuje ostrzeżenie.

Powiedzmy, że chcemy, aby nasza nazwa użytkownika akceptowała tylko ciągi. Musielibyśmy zmodyfikować nasz obiekt props, aby wyglądał tak:

 export default { props: { username: String } }

Weryfikacja właściwości jest niezbędna podczas pracy w dużych aplikacjach Vue lub podczas projektowania wtyczek. Pomaga upewnić się, że wszyscy są na tej samej stronie i używają rekwizytów zgodnie z ich przeznaczeniem.

Aby uzyskać pełną listę weryfikacji, które możemy uwzględnić na rekwizytach, zdecydowanie polecam zapoznanie się z oficjalną dokumentacją, aby uzyskać dogłębną recenzję.

Wskazówka: przestrzegaj konwencji nazewnictwa rekwizytów

Według przewodnika po stylu VueJS najlepszym sposobem na nazwanie swoich rekwizytów jest użycie camelCase podczas deklarowania ich w skrypcie i kebab-case podczas odwoływania się do nich w kodzie szablonu.

Rozumowanie stojące za tym jest w rzeczywistości dość proste. W JavaScript camelCase jest standardową konwencją nazewnictwa, aw HTML jest to kebab-case.

Dlatego Vue zaleca, abyśmy trzymali się norm każdego języka. Na szczęście Vue jest w stanie automatycznie konwertować między dwoma stylami, więc nie ma dodatkowej konfiguracji dla programistów.

 // GOOD <account-info :my-username="user.username" /> props: { myUsername: String } // BAD <account-info :myUsername="user.username" /> props: { "my-username": String }

2. Emitowanie zdarzeń w celu udostępniania danych od dziecka do rodzica

Teraz, gdy mamy dane przekazywane w dół hierarchii, przekażmy je w inny sposób: z komponentu potomnego do rodzica. Nie możemy używać rekwizytów, ale możemy używać niestandardowych zdarzeń i detektorów.

Każda instancja Vue może wywołać metodę .$emit(eventName) , która wyzwala zdarzenie. Następnie możemy nasłuchiwać tego zdarzenia w taki sam sposób jak każdego innego, korzystając z dyrektywy v-on.

Tworzenie niestandardowego wydarzenia

Oprzyjmy się na naszym przykładzie profilu użytkownika, dodając przycisk, który zmienia nazwę użytkownika. Wewnątrz naszego komponentu potomnego ( AccountInfo.vue ), utwórzmy przycisk.

Następnie po kliknięciu tego przycisku wyemitujemy zdarzenie o nazwie changeUsername .

 <template> <div id='account-info'> <button @click='changeUsername()'>Change Username</button> {{username}} </div> </template> <script> export default { props: { username: String }, methods: { changeUsername() { this.$emit('changeUsername') } } } </script>

Wewnątrz rodzica obsługujemy to zdarzenie i zmieniamy zmienną user.username . Tak jak omawialiśmy wcześniej, możemy słuchać zdarzeń za pomocą dyrektywy v-on lub w skrócie „@”.

 <template> <div> <account-info :username="user.username" @changeUsername="user.username = 'new name'"/> </div> </template>

Wypróbujmy to. Powinieneś zobaczyć, że po kliknięciu przycisku nazwa użytkownika zmienia się na „nowa nazwa”.

Wskazówka: zdarzenia niestandardowe mogą akceptować argumenty

Najczęstszym przypadkiem użycia przekazywania argumentów do twoich zdarzeń jest sytuacja, gdy chcesz, aby komponent potomny mógł ustawić określoną wartość dla swojej właściwości. Nigdy nie chcesz bezpośrednio edytować wartości właściwości z samego komponentu.

Jednak na szczęście możemy użyć argumentów pass z naszymi zdarzeniami niestandardowymi, aby zmienić wartości komponentu nadrzędnego.

Załóżmy, że chcemy zmodyfikować zdarzenie changeUsername , abyśmy mogli przekazać mu wartość.

Metoda $emit przyjmuje opcjonalny drugi parametr dla argumentów. Więc wszystko, co robimy, to dodawanie naszej nowej wartości nazwy użytkownika po nazwie naszego wydarzenia.

 this.$emit('changeUsername', 'mattmaribojoc')

Następnie, w naszym komponencie nadrzędnym, możemy albo uzyskać dostęp do tych wartości bezpośrednio, używając specjalnej zmiennej $event , albo możemy napisać metodę obsługi, która pobiera parametr.

 <account-info :username="user.username" @changeUsername="user.username = $event"/> OR <account-info :username="user.username" @changeUsername="changeUsername($event)"/> export default { ... methods: { changeUsername (username) { this.user.username = username; } } }

3. Używanie Vuex do tworzenia stanu współdzielonego na poziomie aplikacji

Dobra — wiemy, jak dzielić się danymi między rodzicami/dziećmi, ale co z innymi komponentami? Czy musimy tworzyć niezwykle złożony system hierarchiczny, jeśli chcemy przekazywać dane?

Na szczęście nie. Wspaniała biblioteka zarządzania stanem Vuex od lat ułatwia życie programistom. Krótko mówiąc, tworzy scentralizowany magazyn danych, do którego mają dostęp wszystkie komponenty.

W metodach, których używaliśmy wcześniej (props / emitting events), każdy komponent ma swój własny stan danych, który następnie dzielimy między komponentami. Jednak Vuex pozwala nam wyodrębnić wszystkie udostępnione dane w jednym stanie, do którego każdy komponent ma łatwy dostęp. Ten wspólny stan nazywa się sklepem.

Wypróbujmy to.

Ponieważ Vuex jest oddzielny od podstawowego kodu Vue, najpierw musimy go zainstalować i zaimportować do naszego projektu. Najpierw musimy uruchomić npm install vuex --save w interfejsie CLI naszego projektu.

Następnie utwórz folder src/store z plikiem index.js zawierającym następujący kod.

 // store/index.js import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); export default new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {} });

Aby uwzględnić to w naszej głównej instancji Vue, musimy zaimportować nasz plik store/index.js i przekazać go do naszego konstruktora Vue.

 // main.js import store from "./store"; new Vue({ store, ...

Uzyskiwanie dostępu do komponentów wewnętrznych sklepu Vue

Ponieważ dodaliśmy nasz sklep Vuex do naszej instancji root Vue, jest on wstrzykiwany do wszystkich dzieci roota. Jeśli chcemy uzyskać dostęp do sklepu z komponentu, możemy skorzystać z this.$store .

Teraz przyjrzyjmy się specyfice każdej z czterech części sklepu Vuec.

1. Państwo

Stan Vuex to obiekt, który zawiera dane na poziomie aplikacji. Wszystkie instancje Vue będą miały dostęp do tych danych.

Dla naszego sklepu utwórzmy obiekt użytkownika, który przechowuje więcej danych profilu użytkownika.

 export default new Vuex.Store({ state: { user: { username: 'matt', fullName: 'Matt Maribojoc' } }, getters: {}, mutations: {}, actions: {} });

Możemy uzyskać dostęp do tych danych w dowolnym komponencie instancji, takim jak ten.

 mounted () { console.log(this.$store.state.user.username); },

2. Gettery

Używamy getterów Vuex, aby zwrócić zmodyfikowaną wartość danych stanu. Dobrym sposobem myślenia o getterach jest traktowanie ich jak właściwości obliczanych. Na przykład gettery, podobnie jak obliczane właściwości, buforują swoje wyniki i ponownie oceniają tylko wtedy, gdy zależność zostanie zmodyfikowana.

Opierając się na naszym wcześniejszym sklepie, powiedzmy, że chcemy stworzyć metodę, która zwraca imię użytkownika na podstawie atrybutu pełnej nazwy.

 getters: { firstName: state => { return state.user.fullName.split(' ')[0] } }

Właściwości gettera Vuex są dostępne dla komponentów w obiekcie store.getters .

 mounted () { console.log(this.$store.getters.firstName); }

Wskazówka: poznaj domyślne argumenty gettera

Domyślnie gettery Vuex akceptują dwa argumenty.

  1. state — obiekt stanu dla naszej aplikacji;
  2. getters — obiekt store.getters, co oznacza, że ​​możemy wywoływać inne gettery w naszym sklepie.

Każdy zadeklarowany przez Ciebie getter będzie wymagał pierwszego argumentu stanu. W zależności od tego, jak zaprojektujesz swój kod, gettery mogą odwoływać się do siebie nawzajem za pomocą drugiego argumentu „gettery”.

Stwórzmy metodę pobierania nazwiska, która po prostu usuwa wartość naszego imienia z naszej właściwości stanu full name. Ten przykład wymagałby zarówno obiektów state, jak i getters.

 lastName (state, getters) { return state.user.fullName.replace(getters.firstName, ''); }

Wskazówka: przekaż niestandardowe argumenty do Vuex Getters

Inną fajną cechą getterów jest to, że możemy przekazać im niestandardowe argumenty, sprawiając, że nasz getter zwraca metodę.

 prefixedName: (state, getters) => (prefix) => { return prefix + getters.lastName; } // in our component console.log(this.$store.getters.prefixedName("Mr."));

3. Mutacje

Mutacje to jedyny sposób na prawidłową zmianę wartości obiektu stanu. Ważnym szczegółem do odnotowania jest to, że mutacje muszą być synchroniczne .

Podobnie jak gettery, mutacje zawsze akceptują właściwość stanu Vuex jako swój pierwszy argument. Akceptują również argument niestandardowy — zwany ładunkiem — jako drugi argument.

Na przykład zróbmy mutację, aby zmienić nazwę użytkownika na określoną wartość.

 mutations: { changeName (state, payload) { state.user.fullName = payload } },

Następnie możemy wywołać tę metodę z naszego komponentu za pomocą metody store.commit , z naszym ładunkiem jako drugim argumentem.

 this.$store.commit("changeName", "New Name");

Najczęściej będziesz chciał, aby Twój ładunek był przedmiotem. Oznacza to nie tylko, że możesz przekazać kilka argumentów do mutacji, ale także sprawia, że ​​twój kod jest bardziej czytelny ze względu na nazwy właściwości w twoim obiekcie.

 changeName (state, payload) { state.user.fullName = payload.newName }

Istnieją dwa różne sposoby wywoływania mutacji z ładunkiem.

  1. Możesz mieć typ mutacji jako pierwszy argument, a ładunek jako drugi.
  2. Można zadeklarować przekazanie pojedynczego obiektu z jedną właściwością dla typu i inną dla ładunku.
 this.$store.commit("changeName", { newName: "New Name 1", }); // or this.$store.commit({ type: "changeName", newName: "New Name 2" });

Nie ma prawdziwej różnicy między tym, jak działają, więc zależy to całkowicie od osobistych preferencji. Pamiętaj, że zawsze najlepiej jest być konsekwentnym w całym projekcie, więc niezależnie od tego, który wybierzesz, trzymaj się go!

4. Działania

W Vuex działania są dość podobne do mutacji, ponieważ używamy ich do zmiany stanu. Jednak działania same w sobie nie zmieniają wartości. Zamiast tego działania powodują mutacje.

Ponadto, chociaż mutacje Vuex muszą być synchroniczne, działania nie. Używając akcji, możemy wywołać mutację na przykład po wywołaniu API.

Podczas gdy większość programów obsługi Vuex, które widzieliśmy, akceptuje stan jako główny parametr, akcje akceptują obiekt kontekstu. Ten obiekt kontekstowy umożliwia nam dostęp do właściwości w naszym sklepie Vuex (np. stan, zatwierdzenie, pobieranie).

Oto przykład akcji Vuex, która czeka dwie sekundy, a następnie wykonuje mutację changeName .

 actions: { changeName (context, payload) { setTimeout(() => { context.commit("changeName", payload); }, 2000); } }

Wewnątrz naszych komponentów używamy metody store.dispatch w celu uruchomienia naszej funkcji. Przekazujemy argumenty, tak jak robiliśmy z mutacjami. Deklarujemy typ i przekazujemy dowolne niestandardowe argumenty w drugim argumencie.

 this.$store.dispatch("changeName", { newName: "New Name from Action" });

Zawijanie

Teraz powinieneś znać trzy różne sposoby udostępniania danych między komponentami w VueJS: rekwizyty, niestandardowe zdarzenia i sklep Vuex.

Mam nadzieję, że ten samouczek pomógł ci uzyskać więcej informacji na temat różnych metod Vue i najlepszych praktyk. Daj znać, jak wdrożyłeś je do swoich projektów!

Dalsza lektura

Jeśli chcesz zagłębić się w techniczną stronę/możliwości każdej techniki, oto kilka świetnych miejsc, od których możesz zacząć.

  • Witryna oficjalnego przewodnika Vuex
  • Dokumenty VueJS dotyczące rekwizytów i zdarzeń niestandardowych
  • „WTF to Vuex? Przewodnik dla początkujących po magazynie danych aplikacji Vue”, Anthony Gore, programiści Vue.js