Vývoj Next Gen Komponentov vo Vue CLI – Riešime Vue.js #2


Vývoj Next Gen Komponentov vo Vue CLI – Riešime Vue.js #2

Na začiatku bol jeden veľký monolit. Jeho poznávacími znameniami boli mohutná veľkosť, kvantum riadkov neotestovateľného kódu a stabilitou horšou než domček z karát. Robiť v tejto štruktúre nejaké zmeny či debugovať bolo úlohou len pre silné povahy. Preto sa monolit začal rozbíjať na menšie časti – elementy, moduly, komponenty a fragmenty. Komponent predstavuje izolovanú logickú časť aplikácie, ktorá by nemala ovplyvňovať iné komponenty.

Vue.js CLI

Vo Vue.js, respektíve v Nuxt, je všetko zložené z komponentov. V komponente s layoutom importujem komponent s contentom, ten sa skladá z množstva ďalších menších komponentov. Teraz sa budem venovať práve týmto najmenším častiam stránky. Každý komponent bude mať svoju logiku, šablónu, CSS, musí byť testovateľný a v ideálnom prípade ho chcem vedieť použiť aj v inej aplikácii. Budem vytvárať viacero menších aplikácií, v (pravdepodobne) samostatných repozitároch. V tejto časti si budeme konkrétne pripravovať komponent – našepkávač, pomocou Vue CLI.

npm install -g @vue/cli

Použitie CLI má viacero výhod, hlavne z dlhodobého vývoja. CLI ti pripraví celú štruktúru, nainštaluje potrebné balíčky, pripraví build, testy, a hlavne, relatívne jednoducho sa rieši aktualizovanie balíčkov. Nedávno vyšla už verzia 4.x a k upgradu je pekne spísaný postup na oficiálnej stránke. U mňa bol upgrade celkom jednoduchý. Inštalovanie novej verzie CLI a spustením príkazu vue upgrade v mojom komponente.

CLI prichádza aj s ďalšími “vychytávkami”. Asi najviac cool je ich UI, ktorým vieš robiť prakticky čokoľvek. Spúšťať skripty, inštalovať dependencies, debugovať,… Spustenie je jednoduché:

vue ui

.

Nový komponent – Našepkávač

Našepkávač sa bude skladať z dvoch častí (komponentov):
– formulár s inputom na vyhľadávanie,
– kontajner pre výsledky vyhľadávania.

Najskôr vytvorím nový projekt:

vue create autocomplete

CLI mi pripraví základnú štruktúru:

Formulár s inputom v sebe nemá žiadnu veľkú logiku, len musí pri zmene svojej value, vyvolať search event v hlavnom komponente.

<template>
  <form method="get" accept-charset="utf-8" :action="actionUrl">
    <input type="text" name="search" @input="search" />
  </form>
</template>
 
<script>
// src/components/Form.vue
 
export default {
  name: "Form",
  props: ["actionUrl"],
  methods: {
    search: function(event) {
      this.$emit("search", event.currentTarget.value);
    }
  }
};
</script>

V kontajneri budem zobrazovať výsledky vyhľadávania. Očakávam dáta v tomto formáte:

{
    "product": {
        "heading": "Produkty",
        "count": 2,
        "matches": [
            {
                "id": 1,
                "name": "Produkt 1",
                "image": "",
                "price": 10
            },
            {
                "id": 2,
                "name": "Produkt 2",
                "image": "",
                "price": 20
            }
        ]
    },
    "category": {
        "heading": "Kategorie",
        "count": 1,
        "matches": [
            {
                "id": 1,
                "name": "Kategoria 1",
                "image": ""
            }
        ]
    }
}

Navyše by som chcel mať zvýraznený vyhľadávaný text v mojich výsledkoch. Preto si nainštalujem komponent vue-highlight-text.

npm install vue-text-highlight --save
<template>
  <div class="autocomplete-container">
    <ul v-for="(group, key) in matches" v-bind:key="key">
      <li>
        <strong>{{ group.heading }}</strong>
      </li>
 
      <li v-for="match in group.matches" v-bind:key="match.id">
        <a href="match.link" title="match.name">
          <vue-text-highlight :queries="keyword.split(' ')">
            {{ match.name }}
          </vue-text-highlight>
        </a>
      </li>
    </ul>
  </div>
</template>
 
<script>
// src/components/Container.vue
 
import TextHighlight from "vue-text-highlight";
 
export default {
  name: "Container",
  props: ["matches", "keyword"],
  components: {
    "vue-text-highlight": TextHighlight
  }
};
</script>

Hlavný komponent bude slúžiť ako layout pre Form a Container. Bude obsahovať metódu pre vyhľadávanie a zobrazenie výsledkov.

<template>
  <div class="autocomplete">
    <slot name="before-form" />
    <autocomplete-form :actionUrl="actionUrl" v-on:search="search" />
    <slot name="after-form" />
 
    <slot name="before-container" />
    <autocomplete-container
      v-if="isOpen"
      :matches="matches"
      :keyword="keyword"
    />
    <slot name="after-container" />
  </div>
</template>
 
<script>
// src/components/Autocomplete.vue
 
const axios = require("axios");
 
import FormComponent from "./Form";
import ContainerComponent from "./Container";
 
export default {
  name: "Autocomplete",
  components: {
    "autocomplete-form": FormComponent,
    "autocomplete-container": ContainerComponent
  },
  props: {
    inputLimit: {
      required: false,
      default: () => {
        return 3;
      }
    },
    requestUrl: {
      required: true,
      default: () => {
        return "/search";
      }
    }
  },
  data: function() {
    return {
      matches: [],
      keyword: "",
      isOpen: false
    };
  },
  methods: {
    search: function(keyword) {
      const _self = this;
 
      _self.keyword = keyword;
 
      if (_self.inputLimit > _self.keyword.length + 1) {
        return;
      }
 
      axios({
        method: "get",
        url: _self.requestUrl,
        responseType: "json",
        params: {
          search: _self.keyword
        }
      }).then(function(response) {
        // handle success
        _self.matches = response.data;
        _self.isOpen = true;
      });
    }
  }
};
</script>

Ako už klasicky, na handlovanie requestov použijem axios. Mohol by som ešte ako bonus vyriešiť error handling. V šablóne som použil takzvané slots (<slot name=“…“ />), ktoré slúžia ako placeholdery na content, ktorý by som chcel do komponentu vložiť. V prípade, že by som do slots potreboval aj vkladať dáta z properties, môžem využiť scoped slots.

Komponent mám ready. Pre účely testovania využijem App.vue, ktorý mi CLI pripravilo. Len počkať. Ako otestujem ajax call? Môžem si napríklad vytvoriť jednoduchý entrypoint v Node/PHP. Preferovanejšia možnosť je mockovanie (imitovanie) requestov cez nejaký nástroj. V článku o testovaní Vue.js komponentov, som používal moxios. Teraz pre zmenu vyskúšam axios-mock-adapter. Nezabudni, že všetky dependencies vieš inštalovať aj pomocou Vue UI.

npm install axios-mock-adapter --save-dev

V src zložke, som vytvoril súbor data.json, ktorý bude obsahovať testovacie dáta. V callbacku created som definoval, že ak príde request na url „/search“, odpovedaj mi s 200 statusom a dátami, z data.json. Rovno môžem vyskúšať, či fungujú definované slots.

<template>
  <div id="app">
    <auto-complete :requestUrl="'/search'">
      <template slot="before-form">
        BEFORE FORM
      </template>
      <template slot="after-form">
        AFTER FORM
      </template>
    </auto-complete>
  </div>
</template>
 
<script>
// src/App.vue
 
const axios = require("axios");
const MockAdapter = require("axios-mock-adapter");
 
const mock = new MockAdapter(axios);
 
import testdata from "./data.json";
import AutocompleteComponent from "./components/Autocomplete";
 
export default {
  name: "app",
  components: {
    "auto-complete": AutocompleteComponent
  },
  created: function() {
    mock.onGet("/search").reply(200, testdata);
  }
};
</script>

Aplikáciu teraz môžem konečne spustiť a CLI mi opäť dáva na výber:

Spustenie klasicky – cez príkaz:

npm run serve

Alebo cez UI:

Keďže som teraz neriešil žiadne CSS, aplikácia nevyzerá nejako prívetivo. O to teraz ani nešlo. Cieľom bolo demonštrovať, ako vytvoriť a otestovať autocomplete komponent. Ďalej je možné funkcionalitu rozširovať o rôzne ďalšie interakcie, ako prechádzanie výsledkov pomocou klávesnice, skrývanie výsledkov vyhľadávania…

Web Components

Ak by som neriešil IE 11, tak web komponenty by mohli byť ďalším krokom vo vývoji znovupoužiteľných elementov stránky. Custom tag s možnosťou properties, HTML šablóna, funkcionalita, CSS… a toto všetko natívne, priamo v mojom browseri. Bližšie detaily si nechám na niektorý z budúcich článkov. Teraz ti ukážem, ako pomocou vue-cli zabaliť môj komponent do WC (krásne to znie).

Priamo v oficiálnej dokumentácii je celý postup pekne popísaný. Postup obsahujúci jediný krok – spustiť príkaz :).

vue-cli-service build --target wc --name auto-complete src/components/Autocomplete.vue

Aby som nemusel vždy toľko písať, príkaz pridám do skriptov v package.json a pomenujem ho ako „buildComponent“. Po spustení príkazu:

V dist zložke CLI vytvorí aj minifikovanú verziu. Len si daj pozor, samotné Vue.js treba pridať na stránku zvlášť, pretože bundler ho defaultne nepribalí, aj keď bolo pôvodne importované. Toto správanie je možné obísť flagom --inline-vue, ale nie je to odporúčané. Stratil by si možnosť cache-ovania dependencies, respektíve by sa veľmi ľahko mohlo stať, že stránka bude omylom obsahovať viacnásobne načítanú knižnicu. Samotný komponent už môžeš použiť na svojej stránke ako klasický HTML tag:

<auto-complete action-url="/search"></auto-complete>

Vue.js v3 ready komponenty – Composition API

Kód komplexnejšieho komponentu vie byť niekedy ťažko čitateľný. V Single File Components je kód organizovaný podľa operácie, čiže buď ide o dáta, metódu, computed property… Pridávaním nových features tieto bloky postupne narastajú a väčšinou už strácam pôvodnú „pozriem – vidím“ výhodu. V novej verzii Vue.js príde očakávaná zmena. Kód bude po novom organizovaný podľa funkcionality. To znamená, že bude možné funkcionalitu zabaliť do samostatnej funkcie, alebo dokonca do extra .js súboru.

Ľahšie to ukážem na jednoduchom príklade:

<template>
  <button @click="increment">
    Count is: {{ state.count }}
  </button>
</template>
 
<script>
import { reactive } from "vue";
 
export default {
  setup() {
    const state = reactive({
      count: 0
    })
 
    function increment() {
      state.count++
    }
 
    return {
      state,
      increment
    }
  }
}
</script>

Funkcia setup bude obsahovať dáta – state a metódy pre danú funkcionalitu, ktoré musia ísť do return, aby ich komponent vedel použiť. V rámci setup nemáš prístup k this.

Ak náhodou už teraz stresuješ, že v novom Vue.js ti nebudú fungovať žiadne komponenty, upokojím ťa. Všetky novinky v novej verzii sú úplne voliteľné a môžeš pokojne programovať tak, ako si bol zvyknutý. Väčšina zmien sa udeje hlavne pod kapotou, ale to budem riešiť v niektorej z ďalších častí. Ak máš záujem vyskúšať Composition API už teraz, postup je celkom jednoduchý. Pokojne cez UI nainštaluj plugin @vue/composition-api.

import Vue from 'vue';
import VueCompositionApi from '@vue/composition-api';
 
Vue.use(VueCompositionApi);

Komponenty sú základným kameňom stránky, a ako si mohol vidieť, ich vývoj neustále napreduje. Hlavne treba začať, pretože hrozba monolitu je veľmi reálna. Či už si sám, alebo pracuješ v tíme, oplatí sa mať svoj codebase pripravený na budúcnosť. Preto je aj veľmi zaujímavá idea Micro Frontends, ktorá aplikuje koncept microservices na Frontend.

Ak máš záujem riešiť Vue.js aj ty, prečítaj si naše predošlé články zo série:

Nuxt aplikácia v kontajneroch

Riešime Vue.js #1

Testovanie Vue.js komponentov

vue.js
axios
moxios
testing

V prípade, že by si nám chcel pomôcť s vývojom ďalšej generácie nášho projektu RSHOP, alebo máš záujem sa od nás niečomu priučiť, neváhaj a ozvi sa nám. Ak náhodou na našej kariérnej stránke kariera.riesenia.com nenájdeš svoju vysnívanú pozíciu, napíš nám priamo na náš e-mail joinus@riesenia.com a radi ťa u nás privítame.

+ Diskusia nemá žiadne príspevky