Reaktywność w Vue
Opublikowany: 2022-03-10W tym artykule przyjrzymy się reaktywności w Vue, jak to działa i jak możemy tworzyć reaktywne zmienne za pomocą nowo utworzonych metod i funkcji. Ten artykuł jest skierowany do programistów, którzy dobrze rozumieją, jak działa Vue 2.x i chcą zapoznać się z nowym Vue 3.
Zbudujemy prostą aplikację, aby lepiej zrozumieć ten temat. Kod tej aplikacji można znaleźć na GitHub.
Domyślnie JavaScript nie jest reaktywny . Oznacza to, że jeśli utworzymy zmienną boy
i odniesiemy się do niej w części A naszej aplikacji, a następnie przystąpimy do modyfikacji boy
w części B, część A nie zostanie zaktualizowana o nową wartość boy
.
let framework = 'Vue'; let sentence = `${framework} is awesome`; console.log(sentence) // logs "Vue is awesome" framework = 'React'; console.log(sentence) //should log "React is awesome" if 'sentence' is reactive.
Powyższy fragment kodu jest doskonałym przykładem niereaktywnej natury JavaScript — stąd, dlaczego zmiana nie jest odzwierciedlona w zmiennej sentence
.
W Vue 2.x props
, computed
i data()
były domyślnie reaktywne, z wyjątkiem właściwości, które nie są obecne w data
podczas tworzenia takich komponentów. Oznacza to, że gdy komponent zostanie wstrzyknięty do DOM, tylko istniejące właściwości w obiekcie data
komponentu spowodują aktualizację komponentu, jeśli i kiedy zmienią się te właściwości.
Wewnętrznie Vue 3 używa obiektu Proxy
(funkcja ECMAScript 6) w celu zapewnienia, że te właściwości są reaktywne, ale nadal zapewnia opcję użycia Object.defineProperty
z Vue 2 do obsługi Internet Explorera (ECMAScript 5). Ta metoda definiuje nową właściwość bezpośrednio na obiekcie lub modyfikuje istniejącą właściwość obiektu i zwraca obiekt.
Na pierwszy rzut oka i ponieważ większość z nas już wie, że reaktywność nie jest niczym nowym w Vue, korzystanie z tych właściwości może wydawać się niepotrzebne, ale Options API ma swoje ograniczenia, gdy mamy do czynienia z dużą aplikacją z funkcjami wielokrotnego użytku w kilku części aplikacji. W tym celu wprowadzono nowy interfejs Composition API, który pomaga w abstrahowaniu logiki w celu ułatwienia czytania i utrzymania bazy kodu. Ponadto możemy teraz z łatwością sprawić, że każda zmienna będzie reaktywna, niezależnie od jej typu danych, korzystając z dowolnych nowych właściwości i metod.
Gdy używamy opcji setup
, która służy jako punkt wejścia dla interfejsu Composition API, obiekt data
, computed
właściwości i methods
są niedostępne, ponieważ instancja komponentu nie została jeszcze utworzona podczas wykonywania setup
. Uniemożliwia to skorzystanie z wbudowanej reaktywności w którejkolwiek z tych funkcji podczas setup
. W tym samouczku poznamy wszystkie sposoby, w jakie możemy to zrobić.
Metoda reaktywna
Zgodnie z dokumentacją metoda reactive
, która jest odpowiednikiem Vue.observable()
w Vue 2.6, może być przydatna, gdy próbujemy utworzyć obiekt, którego wszystkie właściwości są reaktywne (tak jak obiekt data
w opcjach API). Pod maską obiekt data
w interfejsie API Options używa tej metody, aby wszystkie zawarte w nim właściwości były reaktywne.
Ale możemy stworzyć własny reaktywny obiekt w ten sposób:
import { reactive } from 'vue' // reactive state let user = reactive({ "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "[email protected]", "address": { "street": "Kulas Light", "suite": "Apt. 556", "city": "Gwenborough", "zipcode": "92998-3874", "geo": { "lat": "-37.3159", "lng": "81.1496" } }, "phone": "1-770-736-8031 x56442", "website": "hildegard.org", "company": { "name": "Romaguera-Crona", "catchPhrase": "Multi-layered client-server neural-net", "bs": "harness real-time e-markets" }, "cars": { "number": 0 } })
Tutaj zaimportowaliśmy metodę reactive
z Vue, a następnie zadeklarowaliśmy naszą zmienną user
, przekazując jej wartość do tej funkcji jako argument. W ten sposób sprawiliśmy, że user
jest reaktywny, a zatem, jeśli użyjemy user
w naszym szablonie i jeśli obiekt lub właściwość tego obiektu powinna się zmienić, ta wartość zostanie automatycznie zaktualizowana w tym szablonie.
ref
Tak jak mamy metodę, aby obiekty były reaktywne, potrzebujemy również innych samodzielnych wartości pierwotnych (łańcuchy, wartości logiczne, wartości niezdefiniowane, liczby itp.) i tablic. Podczas opracowywania pracowalibyśmy z tymi innymi typami danych, jednocześnie potrzebując ich reaktywności. Pierwszym podejściem, o którym moglibyśmy pomyśleć, byłoby użycie reactive
i przekazanie wartości zmiennej, którą chcemy uczynić reaktywną.
import { reactive } from 'vue' const state = reactive({ users: [], });
Ponieważ reactive
ma głęboką konwersję reaktywną, user
jako usługa również będzie reaktywny, osiągając w ten sposób nasz cel; w związku z tym user
zawsze aktualizowałby w dowolnym miejscu w szablonie takiej aplikacji. Ale dzięki właściwości ref
możemy sprawić, że każda zmienna z dowolnym typem danych stanie się reaktywna, przekazując wartość tej zmiennej do ref
. Ta metoda działa również w przypadku obiektów, ale zagnieżdża obiekt o jeden poziom głębiej niż w przypadku metody reactive
.
let property = { rooms: '4 rooms', garage: true, swimmingPool: false } let reactiveProperty = ref(property) console.log(reactiveProperty) // prints { // value: {rooms: "4 rooms", garage: true, swimmingPool: false} // }
Pod maską ref
pobiera przekazany argument i zamienia go na obiekt z kluczem value
. Oznacza to, że możemy uzyskać dostęp do naszej zmiennej przez wywołanie variable.value
, a także możemy zmodyfikować jej wartość, wywołując ją w ten sam sposób.
import {ref} from 'vue' let age = ref(1) console.log(age.value) //prints 1 age.value++ console.log(age.value) //prints 2
Dzięki temu możemy zaimportować ref
do naszego komponentu i stworzyć zmienną reaktywną:
<template> <div class="home"> <form @click.prevent=""> <table> <tr> <th>Name</th> <th>Username</th> <th>email</th> <th>Edit Cars</th> <th>Cars</th> </tr> <tr v-for="user in users" :key="user.id"> <td>{{ user.name }}</td> <td>{{ user.username }}</td> <td>{{ user.email }}</td> <td> <input type="number" name="cars" v-model.number="user.cars.number" /> </td> <td> <cars-number :cars="user.cars" /> </td> </tr> </table> <p>Total number of cars: {{ getTotalCars }}</p> </form> </div> </template> <script> // @ is an alias to /src import carsNumber from "@/components/cars-number.vue"; import axios from "axios"; import { ref } from "vue"; export default { name: "Home", data() { return {}; }, setup() { let users = ref([]); const getUsers = async () => { let { data } = await axios({ url: "data.json", }); users.value = data; }; return { users, getUsers, }; }, components: { carsNumber, }, created() { this.getUsers(); }, computed: { getTotalCars() { let users = this.users; let totalCars = users.reduce(function(sum, elem) { return sum + elem.cars.number; }, 0); return totalCars; }, }; </script>
Tutaj zaimportowaliśmy ref
, aby utworzyć zmienną reaktywną users
w naszym komponencie. Następnie zaimportowaliśmy axios
, aby pobrać dane z pliku JSON w folderze public
, i zaimportowaliśmy nasz komponent carsNumber
, który będziemy tworzyć później. Następną rzeczą, którą zrobiliśmy, było utworzenie zmiennej reaktywnej users
przy użyciu metody ref
, aby users
mogli aktualizować, gdy tylko zmieni się odpowiedź z naszego pliku JSON.
Stworzyliśmy również funkcję getUser
, która pobiera tablicę users
z naszego pliku JSON za pomocą axios i przypisaliśmy wartość z tego żądania do zmiennej users
. Na koniec utworzyliśmy obliczoną właściwość, która oblicza całkowitą liczbę samochodów, które mają nasi użytkownicy, ponieważ zmodyfikowaliśmy ją w sekcji szablonu.
Należy zauważyć, że podczas uzyskiwania dostępu do właściwości ref
, które są zwracane w sekcji szablonu lub poza setup()
, są one automatycznie odpakowywane. Oznacza to, że .value
refs
aby uzyskać do nich dostęp. Ponieważ users
to tablica, moglibyśmy po prostu użyć users
, a nie users.value
w getTotalCars
.
W sekcji szablonu pokazaliśmy tabelę wyświetlającą informacje o każdym użytkowniku wraz z komponentem <cars-number />
. Ten komponent akceptuje rekwizyt cars
, który jest wyświetlany w wierszu każdego użytkownika jako liczba posiadanych samochodów. Ta wartość jest aktualizowana za każdym razem, gdy wartość cars
zmienia się w obiekcie użytkownika , czyli dokładnie tak, jak działałby obiekt data
lub computed
właściwość, gdybyśmy pracowali z interfejsem API Options.
toRefs
Gdy używamy interfejsu Composition API, funkcja setup
akceptuje dwa argumenty: props
i context
. Te props
są przekazywane z komponentu do setup()
i umożliwiają dostęp do właściwości komponentu z wnętrza nowego interfejsu API. Metoda ta jest szczególnie przydatna, ponieważ pozwala na destrukturyzację obiektów bez utraty reaktywności.
<template> <p>{{ cars.number }}</p> </template> <script> export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { console.log(props); // prints {gender: "female", cars: Proxy} }, }; </script> <style></style>
Aby użyć wartości, która jest obiektem z props
w Composition API, zapewniając jednocześnie zachowanie reaktywności, używamy toRefs
. Ta metoda pobiera obiekt reaktywny i przekształca go w zwykły obiekt, w którym każda właściwość oryginalnego obiektu reaktywnego staje się ref
. Oznacza to, że cars
podpierają…
cars: { number: 0 }
… stałoby się teraz tym:
{ value: cars: { number: 0 }
Dzięki temu możemy korzystać z cars
w dowolnej części interfejsu API konfiguracji, zachowując jednocześnie jego reaktywność.
setup(props) { let { cars } = toRefs(props); console.log(cars.value); // prints {number: 0} },
Możemy obserwować tę nową zmienną za pomocą watch
interfejsu Composition API i reagować na tę zmianę w dowolny sposób.
setup(props) { let { cars } = toRefs(props); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }
toRef
Innym częstym przypadkiem użycia, z jakim możemy się spotkać, jest przekazanie wartości , która niekoniecznie jest obiektem, ale raczej jednym z typów danych, które działają z ref
(tablica, liczba, łańcuch, boolean itp.). Dzięki toRef
możemy stworzyć reaktywną właściwość (tj. ref
) ze źródłowego obiektu reaktywnego. Spowoduje to, że właściwość pozostanie reaktywna i będzie aktualizowana po każdej zmianie źródła nadrzędnego.
const cars = reactive({ Toyota: 1, Honda: 0 }) const NumberOfHondas = toRef(state, 'Honda') NumberOfHondas.value++ console.log(state.Honda) // 1 state.Honda++ console.log(NumberOfHondas.value) // 2
Tutaj stworzyliśmy reaktywny obiekt metodą reactive
, z właściwościami Toyota
i Honda
. Wykorzystaliśmy również toRef
, aby utworzyć zmienną reaktywną z Honda
. Z powyższego przykładu widać, że gdy aktualizujemy Honda
za pomocą obiektu cars
reaktywnych lub NumberOfHondas
, wartość jest aktualizowana w obu przypadkach.
Ta metoda jest podobna, a jednak tak różna od metody toRefs
, którą omówiliśmy powyżej, w tym sensie, że utrzymuje połączenie ze swoim źródłem i może być używana do łańcuchów, tablic i liczb. W przeciwieństwie do toRefs
, nie musimy martwić się o istnienie właściwości w jej źródle w momencie tworzenia, ponieważ jeśli ta właściwość nie istnieje w momencie tworzenia tego ref
i zamiast tego zwraca null
, nadal będzie przechowywana jako prawidłowa właściwość, z wprowadzoną formą watcher
, tak że gdy ta wartość się zmieni, ten ref
utworzony za pomocą toRef
również zostanie zaktualizowany.
Możemy również użyć tej metody do stworzenia reaktywnej właściwości z props
. Wyglądałoby to tak:
<template> <p>{{ cars.number }}</p> </template> <script> import { watch, toRefs, toRef } from "vue"; export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { let { cars } = toRefs(props); let gender = toRef(props, "gender"); console.log(gender.value); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }, }; </script>
Tutaj stworzyliśmy ref
, który byłby oparty na właściwości gender
uzyskanej z props
. Przydaje się to, gdy chcemy wykonać dodatkowe operacje na rekwizycie konkretnego komponentu.
Wniosek
W tym artykule przyjrzeliśmy się, jak działa reaktywność w Vue, korzystając z niektórych nowo wprowadzonych metod i funkcji z Vue 3. Zaczęliśmy od przyjrzenia się, czym jest reaktywność i jak Vue używa zakulisowego obiektu Proxy
, aby to osiągnąć. Przyjrzeliśmy się również, jak możemy tworzyć obiekty reaktywne za pomocą reactive
i jak tworzyć reaktywne właściwości za pomocą ref
.
Na koniec przyjrzeliśmy się, jak przekształcić reaktywne obiekty w zwykłe obiekty, z których każda właściwości jest ref
wskazującą na odpowiednią właściwość oryginalnego obiektu, i widzieliśmy, jak utworzyć ref
dla właściwości w reaktywnym obiekcie źródłowym.
Dalsze zasoby
- „Proxy” (obiekt), MDN Web Docs
- „Podstawy reaktywności”, Vue.js
- „Odniesienia”, Vue.js
- „
setup
modułu Lifecycle Hook Registration Inside”, Vue.js