image
https://unsplash.com/photos/a0U58gHCVRw

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:

  • Części składowe komponentu
  • Obsługa zdarzeń i metody
  • Elementy formularza - binding
  • Dyrektywy (v-if, v-for)

Konfiguracja projektu

Przed rozpoczęciem nauki stwórz bazowy projekt przy użyciu VueCLI .

  1. Instalacja Vue CLI
npm install -g @vue/cli-service-global
# lub
yarn global add @vue/cli-service-global
  1. Generowanie projektu

    vue create hello-world
  2. Użyj domyślnych ustawień czyli babel, eslint.

Komponent

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:

Props

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

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;

Zdarzenia

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="...".

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>

Elementy formularza - input

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.

v-if, v-else, v-else-if

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.

Pętle 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>
Uwaga do połączenia 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