W Vue mamy 2 typy komponentów: stanowe (czyli te których używamy domyślnie) oraz funkcyjne (bezstanowe). Komponenty możemy definiować na 2 sposoby. W tym artykule przedstawiam sposób użycia komponentów bezstanowych przy użyciu dwóch rodzajów zapisu. Postaram się pokazać zalety oraz wady obu podejść oraz podzielić się własnymi spostrzeżeniami.
Jest to 1 z 2 wpisów na temat rodzaju komponentów w Vue.
Vue przyzwyczaja nas do wygodnego tworzenia komponentów w jednym pliku (SFC - single file component) za pomocą zapisu analogicznego do języka HTML. Jest to zapis intuicyjny i łatwy do nauki. Zdarzają się jednak sytuacje, że ta forma definiowania widoków jest niewygodna lub wręcz utrudnia zrealizowanie pewnych funkcjonalności. Pomocą może być funkcja render
(RF), która umożliwia definiowanie elementów naszego komponentu za pomocą czystego języka JavaScript.
Na wstępie wspomnę, że nie ma różnicy w ostatecznym sposobie funkcjonowania komponentu zarówno przy użyciu SFC czy funkcji render
. Template'y Vue są kompilowane do funkcji render
, więc tworząc ten sam komponent na 2 sposoby otrzymamy dokładnie ten sam wynik.
Drugim interesującym faktem może być informacja co zwraca funkcja render
. W wyniku jej wywołania otrzymamy obiekt virtual node (VNode), czyli element budujący VirtualDOM. To właśnie VNode'y będące wynikiem kompilacji komponentów tworzą VirtualDOM, który pozwala na efektywne śledzenie i propagowanie zmian.
Przechodząc do praktycznego przykładu, załóżmy, że naszym celem jest stworzenie komponentu, który w zależności od parametru będzie komponentem<router-link>
, elementem <a>
lub <button>
. Komponent dla router-link
i a
ma przyjąć props to
, a dla button
zwrócić event @click
.
Przyjrzyjmy się implementacji takiego komponentu. W katalogu components
zostały umieszczone 2 pliki:
button-link.vue
oraz button-link.js
.
W pliku button-link.vue
w tradycyjny sposób tworzymy komponent, który przy użyciu dyrektywy v-if
wyświetla odpowiedni rodzaj buttona lub linku. Warto w tym miejscu zwrócić uwagę na słowo functional
w tagu <template>
. Ten zapis jest analogiczny to zapisu functional: true
w części <script>
, więc można ich używać wymiennie.
Nie mamy dostępu do this
, a wszystkie parametry komponentu są przekazywane przez obiekt context
. Zatem do propsów odwołujemy się po prostu props.my_value
.
Inaczej też wygląda przypisywanie eventów. Mamy do dyspozycji pole listeners
, które przekazuje eventy przypięte do naszego komponentu. Należy je zatem przypisać do buttona taki sposób v-on="listeners"
Funkcja isLink
jest dostępna przez pole $options
i nie ma dostępu do propsów komponentu, więc należy je przekazać przez parametr.
Brakuje mi dokładnej wiedzy, czy można to zrobić lepiej. W niektórych przykładach można się spotkać, z tworzeniem obiektu `methods`, w którym umieszczamy funkcje. Wtedy dostęp do funkcji `isLink` byłby możliwy w ten sposób `$options.methods.isLink()`. Nic to jednak nie zmienia, i wygląda tylko na zachowanie analogi i konwencji nazewnictwa w stosunku do komponentów stanowych.
Pozostaje jeszcze jeden problem, który nie jest widoczny w tym przykładzie, a powstanie gdy użyjemy zamiast komponentu <router-link>
innego komponentu stanowego lub wcześniej niezarejestrowanego w global scope. Komponent funkcyjny nie "rozumie" pola components
i nie pozwoli zarejestrować wewnątrz siebie innego komponentu :sorry . Nie dostaniesz nawet informacji o błędzie.
Przejdźmy do użycia funkcji render
. W komponentach funkcyjnych otrzymuje 2 parametry: funkcję callback zwracającą VNode
oraz obiekt context
.
render: function (createElement, context) {}
Po dokładny opis funkcji createElement
odsyłam do dokumentacji https://vuejs.org/v2/guide/render-function.html#createElement-Arguments .
W naszym przykładzie w funkcji render sprawdzamy czy jest zdefiniowany parametr to
i przekazujemy odpowiedni element/komponent do funkcji createElement
. W drugim parametrze przekazujemy obiekt z dodatkowymi klasami, listenerami, natomiast w trzecim zawartość np. string, VNode lub w naszym wypadku context.slots().default
.
Oba komponenty różnią się zapisem, ale działają identycznie.
Podsumujmy powyższy przykład w dwóch kategoriach - pod kątem użyteczności funkcji render
w stosunku do <template>
oraz pod kątem użyteczności samych komponentów bezstanowych (funkcyjnych).
Zalety użycia funkcji render:
Wady użycia funkcji render:
createElement
.Zalety komponentów funkcyjnych:
Wady: