Na początku tego kursu, chciałbym Cię zapoznać z podstawową składnią oraz budową komponentów w Vue, kilkoma dyrektywami oraz przechwytywaniem podstawowych zdarzeń od użytkownika.
Zagadnienia które zostaną poruszone:
Przed rozpoczęciem nauki stwórz bazowy projekt przy użyciu VueCLI .
npm install -g @vue/cli-service-global
# lub
yarn global add @vue/cli-service-global
Generowanie projektu
vue create hello-world
We wstępie został wygenerowany bazowy projektu przy użyciu VueCLI.
Wykorzystajmy więc, na początek, komponent HelloWorld
, wygenerowany przez CLI na którym będziemy testować kilka zagadnień.
Po wyczyszczeniu zbędnych rzeczy i dodaniu funkcji data()
powinien wyglądać następująco:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h3>Val: {{ value }}</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data() {
return {
value: 0,
}
}
}
</script>
Przeanalizujmy z czego składa się nasz komponent:
Obiekt props
pozwala na przekazanie do komponentu danych od rodzica. Propsy zawsze muszą zostać zdefiniowane jeśli chcemy ich użyć w komponencie.
Do propsów odwołujemy się przez słowo this
np. this.msg
. Należy pamiętać o pominięciu this
w szablonie widoku, ponieważ kontekst jest ustawiony domyślnie na nasz komponent.
Propsy przekazane do komponentu są reaktywne co oznacza, że aktualizują się automatycznie po zmianie pochodzącej od rodzica i uaktualniają np. widok o nową wartość.
Jak zobaczysz w kolejnych przykładach jest to bardzo pomocne.
Wspomniany wyżej komponent HelloWorld ma zdefiniowany props msg
, więc wywołanie komponentu HelloWorld z poziomu jego rodzica (App.vue) będzie wyglądać następująco:
<template>
<HelloWorld :msg="myStringWithMessage" /> // Wartość pochodząca ze zmiennej //
lub <HelloWorld msg="Helllo" /> // Wartość statyczna
</template>
W tych dwóch wywołaniach jest tylko jedna różnica - ":", który decyduję czy do propsa przypiszemy zmienną czy wartość.
Zmienna myStringWithMessage
może być np. zmienną przechowywaną w data
.
data
jest to miejsce gdzie możemy trzymać nasze wewnętrzne zmienne. Jest to wewnętrzny stan komponentu, do którego nie ma dostępu z zewnątrz. W celu odwołania się do pola w data
wystarczy użyć this
.
Zmienne zdefinowane w data
również są reaktywne o czym za chwilę się przekonasz.
W przeciwieństwie do objektu props
, data musi być funkcją zwracającą obiekt, ponieważ w innym wypadku nie zostanie poprawnie zbindowana do komponentu i this
"nie będzie jej poprawnie widział".
Musisz pamiętasz, aby unikać konfliktu nazw pomiędzy obiektem props
, a data
. Linter na pewno zwróci na to uwagę.
Jeśli znasz reacta na pewno korzystałeś z obiektu props lub state w poniższy sposób.
this.props.myPropValue;
this.state.myStateValue;
w Vue jest po prostu
this.myPropValue;
this.myDataValue;
Dodajmy teraz obsługę podstawowego zdarzenia (kliknięcia) w button. W tym celu dodajmy obiekt methods
w którym zdefinujemy funkcję wywoływaną po kliknięciu w element button.
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h3>Val: {{ value }}</h3>
+ <button @click="onBtnClick">Click me</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data() {
return {
value: 0,
}
},
+ methods: {
+ onBtnClick() {
+ this.value++;
+ }
+ }
}
</script>
W obiekcie methods
definiujemy funkcję, a następnie bindujemy ją do elementu button
podając nazwę funkcji w następujący sposób @click="..."
.
W dokumentacji możesz spotkać się z zapisem `v-on:click`, który jest rozwinięciem zapisu `@click`. W istocie do dyrektywy `v-on` po ":" przekazujemy rodzaj eventu, który chcemy śledzić, dlatego śledzienie np. wciśnięcia klawisza enter będzie zrealizowane w podobny sposób: `v-on:keyup.enter`
Jeśli chcemy przekazać parametr do funkcji powinniśmy przekształcić kod do następującej postaci:
<template>
<button @click="onBtnClick(myArgument)">Click me</button>
</template>
<script>
// ...
methods: {
onBtnClick(myArgument) {
// use myArgument
this.value++;
}
}
</script>
Chcąc otrzymać dostęp do pierwotnych właściwości eventu klikniętego elementu, możesz skorzystać z wbudowanej zmiennej `$event`, którą należy przekazać jako argument wywoływanej funkcji np. `onBtnClick($event, myArgument)`.
Pozostając w tematyce przechwytywania akcji użytkownika, przyjrzyjmy się mechanizmowi obsługi elementów formularzy. Do "powiązania" elementu input z komponentem Vue możemy użyć dyrektywy v-model
.
Przeanalizujmy poniży kod:
<template>
<input v-model="myInput" />
</template>
<script>
// ...
data() {
return {
myInput: undefined,
}
},
</script>
Proste przypisanie zmiennej z data
spowoduje, że wszystko co wpiszemy do inputa zostanie przypisane do naszej zmiennej.
Zachęcam do przyjrzenia się zachowaniu innych typów inputów tutaj.
Powyższy przykład pokazuje jedną z głównych funkcjonalności Vue - reaktywność (ang. reactivity), o której wspomniałem przy okazji definiowania propsów. Dzięki tej reaktywności nie musimy się martwić o ręczne aktualizowanie stanu zmiennych w komponencie oraz powiązanych komponentów.
Są od tego oczywiście wyjątki, ale nie jest to jeszcze czas na zaprzątanie sobie nimi głowy. Dociekliwych zachęcam do lektury https://vuejs.org/v2/guide/reactivity.html#ad.
Dyrektywa v-if oraz v-else pozwala decydować czy jakiś element ma zostać wyświetlony. Składnia wygląda następująco:
<template>
<div>
<span v-if="myConditionIsTrue">Visible on true</span>
<span v-else>Visible on false</span>
</div>
</template>
Ważne jest, aby v-else
wystąpiło bezpośrednio bo bloku który ma dyrektywy v-if
. Jeśli znajdzie się coś pomiędzy nimi, otrzymamy błąd.
v-else-if
jest rozszerzeniem v-if
. Wystarczy w miejsce v-else
wstawić v-else-if="warunek"
.
Użyteczne jest połączenie w/w dyrektyw z blokiem <template>
. Pozwoli to na kontrolę renderowania więcej niż jednego elementu, bez ryzyka wstawienia dodatkowego markup'u w przeciwieństwie do użycia elementów typu div
lub span
.
v-for
Dyrektywa v-for
pozwala na renderowanie tablic oraz obiektów.
Dyrektywę należy użyć na elemencie, który ma zostać powtórzony X razy. W poniższym przykładzie values
jest tablicą, a do val
zostaje przypisany aktualny element. of
oraz in
możesz używać wymiennie.
Dodatkowo podając drugi argument, możesz pobrać aktualny index.
<template>
<div>
<span v-for="(val, index) in values">
{{ index }} {{ val }}
</span>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data() {
return {
values: [1,2,3,4,5,6],
}
}
}
</script>
Każdy element powinien mieć przypisany atrybut key
z identyfikatorem w postaci stringu lub wartości numerycznej. Pozwala to Vue lepiej śledzić zmiany na liście i poprawić wydajność.
Przyjrzyjmy się jeszcze iteracji po obiekcie. Zasadniczą różnicą będą argumenty podawane podczas wywołania. Pierwszym parametrem będzie wartość, drugim nazwa (klucz), a trzecim indeks. Przyjrzyjmy się przykładowi:
<template>
<div>
<span v-for="(val, key, index) in values">
{{ index }} : {{ key }} : {{ val }}
</span>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data() {
return {
values: {
name: "Jon",
surname: "Snow",
age: "15"
},
}
}
}
</script>
v-for
z v-if
Zdecydowanie unikaj użycia obu dyrektyw na tym samym elemencie, jeśli nie jest to celowe i przemyślane działanie. Krótko mówiąc, v-for
ma wyższy priorytet od v-if
i zostanie wywołane jako pierwsze. W efekcie warunek zawarty w v-if
będzie wykonywany przy każdym kroku iteracji oraz przy każdym re-renderze. Lepszy efekt osiągniejsz przenosząc v-if
nad iterowany element dodając go na przykład do elementu . Unikniesz przy okazji problemów z przejrzystością kodu. Więcej o tym zagadnieniu możesz doczytać https://vuejs.org/v2/style-guide/#Avoid-v-if-with-v-for-essential .