Praktické príklady s Vue – 4/x

Tento príklad je už je trochu iný. Kým prvé tri viac predstavovali, ako vysvetľovali, toto je prvý z príkladov, ktoré menej ukážu, ale o to viac vysvetlia. A tento krát je kód aj okomentovaný, aby bol text dával o to väčší zmysel.

Asynchrónny komponent

Pointou tohoto príkladu je vysvetlenie, ako sa efektívne vysporiadať s definovaním komponentov v SPA. Single-page aplikácia nutne neznamená mať aj definície komponentov v jednom súbore. Bavíme sa o single-page, nie single-file… Vyhádzaním definícií komponentov do externých súborov získate neskutočne mnoho … napríklad “objektový prístup” – Fero bude opravovať svoj zmršený komponent, Jožo zasa svoj a nie aby druhý čakal kým prvý neskončí v jedinom súbore. Ďalej tým získate rýchlejšie načítanú svoju SPA. Len tie komponenty ktoré je nutné vykresliť na konkrétnej podstránke budú natiahnuté na pozadí – proste asynchrónne komponenty sa nahrávajú až keď ich je treba. A tie ktoré budú nahrané, ostanú nakešované – žiadne nahrávanie stále dokola pri vrátení sa na už navštívenú podstránku. A také niečo, ako zakomponovať do SPA loader animation kým sa nahrá obsah takéhoto asynchrónneho komponentu / stránky, vôbec nemusíte riešiť po svojom. Nižšie v kóde uvidíte, ako to zas raz rieši za vás Vue priam trápne jednoducho…

<style>
/* Predtým ako vložíš nový element do DOM, pridaj mu túto triedu */
.fade-enter { opacity: 0; transform: translateY(30px); }

/* A aj túto triedu */
.fade-enter-active { transition: all 0.4s; }

/* Po prídaní týchto tried je element vložený do DOM, ale priesvitný
   a posunutý nadol. Lenže hneď po vložení do DOM, jeden frame na to,
   sa mu zároveň odoberie enter trieda. Takže štýl elementu sa vráti
   na default: nepriesvitný a neposunutý. Avšak trieda enter-active mu
   ostala a je v nej predsa transition! Takže sa štýl nevráti na default
   skokom, ale rozanimovane...

/* ------------------------------------------------------------------------ */

/* Predtým ako odoberieš tento element z DOM, pridaj mu túto triedu */
.fade-leave-active { transition: all 0.4s; }

/* Nie zároveň, ale jeden frame na to, mu pridaj aj túto triedu */
.fade-leave-to { opacity: 0; transform: translateY(-30px); }

/* Tu je postup nasledovný: pred odobraním elementu z DOM sa mu pridá
   leave-active trieda. Jeden frame na to sa mu následne prídá aj
   leave-to trieda. Čiže sa zmení štýl elementu na priesvitný a posunutý
   nahor, avšak zasa nie skokom, lebo predtým sa mu triedou leave-active
   pridal transition, takže aj v tomto prípade zmena na nový štýl prebehne
   rozanimovane... */
</style>

<div id="app">
  Príklad reštartujte prepnutím<br>
  napríklad ná záložku HTML a následne<br>
  naspäť na záložku Result.
  <!-- Transition komponent zabezpečí, že keď sa child komponent
       bude meniť, či už pridávať, alebo odoberať, tak predtým ako
       sa ten vnorený element pridá, alebo vymaže z DOM, najskôr
       na ňom prebehnú CSS animácie, pridávaním, alebo odoberaním
       príslušných tried, napárovaných cez name atribút. A mode
       atribút je príkaz, aby sa pri zmene child komponentu najkôr
       odobral starý, vrátane počkania na jeho animáciu a až potom
       vložil do DOM nový element, na ktorom následne tiež môže,
       či nemusí prebehnúť animácia. Skúste ten atribút vymazať
       a pochopíte... -->
  <transition name="fade" mode="out-in">
    <!-- Vykresli komponent, ktorý je špeciálny tým, že v tomto
         súbore nie je jeho definícia. -->
    <async txt="TEST" :num="12" :obj="{key1: 'val1'}" :arr="['Bu!']"></async>
  </transition>
</div>

<script src="https://unpkg.com/vue@2.4.4/dist/vue.js"></script>

<script>
  /* Toto je akože definícia komponentu uložená v externom
     súbore v json tvare. */
  var json = {
    props: ['txt', 'num', 'obj', 'arr'],
    template: '<p>{{txt}}, {{num}}, {{obj.key1}}, {{arr[0]}}</p>'
  }

  // Pomocná funkcia slúžiaca na nahrávanie definície komponentov.
  function loadFromFile(uri) {
    /* Parameter uri tu však odignorujeme a trvanie nahrávania
       komponentu nasimulujeme so setTimeout */
    return new Promise(function (res, rej) {
      setTimeout(function () {
        res(json)
      }, 3000)
    })
  }

  /* Tento lokálne registrovaný komponent bude zobrazený počas
     nahrávania obsahu asynchrónneho komponentu. */
  var loader = {
    template: '<p>Nahrávam...</p>' // Alebo div so spinnerom...
  }

  /* Tento komponent, vrátane šablóny bude nahraný asynchrónne
     zo súboru pomocou funkcie loadFromFile. Ale za a) len ak ho
     vôbec bude treba vykresliť a za b) keď sa nahrá, ostane
     nakešovaný. Žiadne znovu nahrávanie. */
  Vue.component('async', () => ({
    // Pozor, funkcia loadFromFile musí vrátiť Promise.
    component: loadFromFile('path-to-component'),
    // Kým sa nahrá obsah z externého súboru, zobraz komponent loader.
    loading: loader,
    // Ale loader zobraz len ak nahrávanie potrvá dlhšie ako 0.2 sekundy.
    delay: 200
  }))

  new Vue({
    el: '#app'
  })
</script>

[iframe src=”https://jsfiddle.net/provuecateur/oh6b5od9/embedded/result,html,css,js/” width=”100%” height=”600″]