12. Projekt [vuejs-10]: wtyczka [dao], asynchroniczne żądania HTTP
Struktura drzewa projektu [vuejs-10] wygląda następująco:

Projekt [vuejs-10] przedstawia komponent wysyłający żądanie HTTP do serwera zdalnego. Zastosowana architektura wygląda następująco:

Komponent [Vue.js] wykorzystuje warstwę [dao] do komunikacji z serwerem obliczającym podatek.
12.1. Instalacja zależności
Aplikacja [vuejs-10] wykorzystuje bibliotekę [axios] do wysyłania asynchronicznych żądań do serwera obliczającego podatek. Należy zainstalować tę zależność:

- w pliku [4-5], wiersz dodany do pliku [package.json] po zainstalowaniu biblioteki [axios] [1-3];
12.2. Klasa [Dao]
Klasa [Dao] to ta, która została opracowana w akapicie dotyczącym klasy [La classe Dao]. Przytaczamy ją tutaj dla przypomnienia:
'use strict';
// importy
import qs from 'qs'
// klasa [Dao]
class Dao {
// konstruktor
constructor(axios) {
this.axios = axios;
// plik cookie sesji
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// inicjowanie sesji
async initSession() {
// opcje żądania HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// parametry URL
params: {
action: 'init-session',
type: 'json'
}
};
// wykonanie zapytania HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// opcje zapytania HHTP [post /main.php?action=authentifier-utilisateur]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// treść POST
data: qs.stringify({
user: user,
password: password
}),
// parametry URL
params: {
action: 'authentifier-utilisateur'
}
};
// wykonanie zapytania HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// opcje zapytania HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// parametry zapytania URL
params: {
action: 'get-admindata'
}
};
// wykonanie zapytania HTTP
const data = await this.getRemoteData(options);
// wynik
return data;
}
async getRemoteData(options) {
// dla pliku cookie sesji
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// wykonanie zapytania HTTP
let response;
try {
// żądanie asynchroniczne
response = await this.axios.request('main.php', options);
} catch (error) {
// parametr [error] jest instancją wyjątku – może przybierać różne formy
if (error.response) {
// odpowiedź serwera znajduje się w [error.response]
response = error.response;
} else {
// błąd jest ponownie generowany
throw error;
}
}
// odpowiedź to cała odpowiedź serwera w formacie HTTP (nagłówki HTTP + sama odpowiedź)
// pobieramy plik cookie sesji, jeśli istnieje
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie to tablica
// w tej tablicy szuka się pliku cookie sesji
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// szukamy pliku cookie sesji
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// zapisujemy plik cookie sesji
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// znaleziono
trouvé = true;
} else {
// kolejny element
i++;
}
}
}
// odpowiedź serwera znajduje się w [response.data]
return response.data;
}
}
// eksport klasy
export default Dao;
Projekt [vuejs-10] wykorzystuje wyłącznie metodę asynchroniczną [initSession] z wierszy 18–30. Przypominamy, że klasa [Dao] jest instancjonowana z parametrem [axios] (wiersz 10), który jest inicjowany przez kod wywołujący. Kodem wywołującym będzie w tym przypadku skrypt [./main.js].
12.3. Wtyczka [pluginDao]
Wtyczka [pluginDao] ma następującą postać:
export default {
install(Vue, dao) {
// dodaje właściwość [$dao] do klasy Vue
Object.defineProperty(Vue.prototype, '$dao', {
// gdy odwołuje się do Vue.$dao, zwracany jest drugi parametr [dao]
get: () => dao,
})
}
}
Jeśli przypomnimy sobie wyjaśnienie podane dla wtyczki [event-bus], widzimy, że wtyczka [pluginDao] tworzy w klasie / funkcji [Vue] nową właściwość o nazwie [$dao]. Wartością tej właściwości będzie (co pozostaje jeszcze do wykazania) obiekt eksportowany przez skrypt [./Dao], czyli poprzednia klasa [Dao].
12.4. Główny skrypt [main.js]
Kod głównego skryptu [main.js] jest następujący:
// importy
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios';
// wtyczki
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);
// bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
// warstwa [dao]
import Dao from './Dao';
// konfiguracja axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/podatki/wersja-14';
axios.defaults.withCredentials = true;
// instancja warstwy [dao]
const dao = new Dao(axios);
// wtyczka [dao]
import pluginDao from './plugins/dao'
Vue.use(pluginDao, dao)
// konfiguracja
Vue.config.productionTip = false
// instancja projektu [App]
new Vue({
render: h => h(App),
}).$mount('#aplikacja')
Skrypt [main.js]:
- instancjonuje warstwę [dao] w wierszach 14–21;
- dołącza wtyczkę [pluginDao] w wierszach 24–25;
- w wierszu 15: importowana jest klasa [Dao];
- wiersze 17–18: konfiguruje się obiekt [axios], który realizuje zapytania HTTP. Obiekt ten jest importowany w wierszu 4;
- wiersz 17: definiuje się obiekt [timeout] o czasie trwania 2 sekund;
- wiersz 18: obiekt URL serwera obliczającego podatek;
- wiersz 19: umożliwia wymianę plików cookie z serwerem;
- wiersze 24–25: użycie wtyczki [pluginDao]
- wiersz 24: import wtyczki;
- wiersz 25: integracja wtyczki. Widać, że drugim parametrem metody [Vue.use] jest odwołanie do warstwy [dao] zdefiniowanej w wierszu 21. Z tego powodu właściwość [Vue.$dao] będzie odnosić się do warstwy [dao] we wszystkich instancjach klasy / funkcji [Vue], tj. we wszystkich komponentach [Vue.js];
12.5. Widok główny [App.vue]
Kod widoku głównego [App] jest następujący:
<template>
<div class="container">
<b-card>
<!-- komunikat -->
<b-alert show variant="success" align="center">
<h4>[vuejs-10] : plugin [dao], requêtes HTTP asynchrones</h4>
</b-alert>
<!-- komponent wysyłający asynchroniczne zapytanie do serwera obliczającego podatek-->
<Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
<!-- wyświetlanie ewentualnego błędu -->
<b-alert show
variant="danger"
v-if="showError">Evénement [error] intercepté par [App]. Valeur reçue = {{error}}</b-alert>
<!-- komunikat o oczekiwaniu z ikoną ładowania -->
<b-alert show v-if="showWaiting" variant="light">
<strong>Requête au serveur de calcul d'impôt en cours...</strong>
<b-spinner variant="primary" label="Spinning"></b-spinner>
</b-alert>
</b-card>
</div>
</template>
<script>
import Component1 from "./components/Component1";
export default {
name: "app",
// stan komponentu
data() {
return {
// sterowanie ikoną ładowania
showWaiting: false,
// sterowanie wyświetlaniem błędu
showError: false,
// przechwycony błąd
error: {}
};
},
// użyte komponenty
components: {
Component1
},
// metody zarządzania zdarzeniami
methods: {
// początek oczekiwania
beginWaiting() {
// wyświetlanie stanu oczekiwania
this.showWaiting = true;
// ukrywanie komunikatu o błędzie
this.showError = false;
},
// koniec oczekiwania
endWaiting() {
// ukrywanie oczekiwania
this.showWaiting = false;
},
// obsługa błędu
doSomethingWithError(error) {
// odnotowuje się wystąpienie błędu
this.error = error;
// wyświetlanie komunikatu o błędzie
this.showError = true;
}
}
};
</script>
Komentarze
- wiersz 9: [Component1] to komponent, który wysyła asynchroniczne zapytanie HTTP. Może on generować trzy zdarzenia:
- [beginWaiting]: żądanie zostanie wysłane. Należy wyświetlić użytkownikowi komunikat o oczekiwaniu;
- [endWaiting]: żądanie zostało zakończone. Należy zakończyć oczekiwanie;
- [error]: żądanie zakończyło się niepowodzeniem. Należy wyświetlić komunikat o błędzie;
- wiersze 10–13: alert wyświetlający ewentualny komunikat o błędzie. Jest on sterowany przez zmienną logiczną [showError] z wiersza 33. Wyświetla błąd z wiersza 35;
- wiersze 14–18: alert wyświetlający komunikat o oczekiwaniu wraz z ikoną ładowania. Jest on sterowany przez zmienną logiczną [showWaiting] z wiersza 47;
- wiersze 45–50: [beginWaiting] to metoda uruchamiana po otrzymaniu zdarzenia [beginWaiting]. Wyświetla komunikat o oczekiwaniu (wiersz 47) i ukrywa komunikat o błędzie (wiersz 49) na wypadek, gdyby był on widoczny w wyniku poprzedniej operacji;
- wiersze 52–55: [endWaiting] to metoda uruchamiana po otrzymaniu zdarzenia [endWaiting]. Ukrywa ona komunikat oczekiwania (wiersz 54);
- wiersze 57–62: [doSomethingWithError] to metoda uruchamiana po otrzymaniu zdarzenia [error]. Rejestruje ona otrzymany błąd (wiersz 59) i wyświetla komunikat o błędzie (wiersz 61);
12.6. Komponent [Component1]
Kod komponentu [Component1] jest następujący:
<template>
<b-row>
<b-col>
<b-alert show
variant="warning"
v-if="showMsg">Valeur reçue du serveur = {{data}}</b-alert>
</b-col>
</b-row>
</template>
<script>
export default {
name: "component1",
// stan komponentu
data() {
return {
showMsg: false
};
},
// metody obsługi zdarzeń
methods: {
// przetwarzanie danych otrzymanych z serwera
doSomethingWithData(data) {
// zapisywanie otrzymanych danych
this.data = data;
// wyświetlanie danych
this.showMsg = true;
}
},
// komponent został właśnie utworzony
created() {
// inicjowanie sesji z serwerem – żądanie asynchroniczne
// wykorzystuje się obietnicę zwróconą przez metody warstwy [dao]
// zgłaszany jest początek operacji
this.$emit("beginWaiting");
// uruchamia się operację asynchroniczną
this.$dao
// chodzi o zainicjowanie sesji jSON z serwerem obliczającym podatek
.initSession()
// metoda przetwarzająca otrzymane dane w przypadku powodzenia
.then(data => {
// przetwarzanie otrzymanych danych
this.doSomethingWithData(data);
})
// metoda obsługująca błąd w przypadku niepowodzenia
.catch(error => {
// przekazywanie błędu do komponentu nadrzędnego
this.$emit("error", error.message);
}).finally(() => {
// koniec oczekiwania
this.$emit("endWaiting");
})
}
};
</script>
Uwagi
- wiersze 4–6: komponent składa się z jednego powiadomienia, które wyświetla wartość zwróconą przez serwer obliczający podatek, i to wyłącznie w przypadku pomyślnego zakończenia zapytania HTTP. Ten komunikat jest kontrolowany przez zmienną logiczną [showMsg] w wierszu 17;
- wiersze 31–53: zapytanie HTTP jest wysyłane zaraz po utworzeniu komponentu. Jego kod umieszcza się zatem w metodzie [created] w wierszu 31;
- wiersz 35: informujemy komponent nadrzędny, że zapytanie asynchroniczne zostanie uruchomione;
- wiersze 37–39: wykonywana jest metoda [this.$dao.initSession]. Inicjuje ona sesję jSON z serwerem obliczającym podatek. Natychmiastowym wynikiem tej metody jest [Promise];
- wiersze 41–44: ten kod jest wykonywany, gdy serwer zwrócił wynik bez błędu. Wynik z serwera znajduje się w [data]. W wierszu 43 wywołuje się metodę [doSomethingWithData] w celu przetworzenia tego wyniku;
- wiersze 46–49: ten kod jest wykonywany w przypadku wystąpienia błędu podczas realizacji zapytania. W wierszu 48 informujemy komponent nadrzędny o wystąpieniu błędu i przekazujemy mu komunikat o błędzie [error.message];
- wiersze 49–52: ten kod jest wykonywany w każdym przypadku. Informuje się komponent nadrzędny, że żądanie HTTP zostało zakończone;
- wiersze 23–28: metoda [doSomethingWithData] służy do przetwarzania danych [data] przesłanych przez serwer. W wierszu 25 zapisujemy te dane, a w wierszu 27 je wyświetlamy;
12.7. Wykonanie projektu

Jeśli podczas uruchamiania projektu serwer obliczający podatek nie jest uruchomiony, otrzymujemy następujący wynik:

Uruchommy serwer [Laragon] (patrz https://tahe.developpez.com/tutoriels-cours/php7) i odświeżmy powyższą stronę. Wynik będzie wówczas następujący:

Uwaga: korzystamy tutaj z wersji 14 serwera obliczającego podatek, zdefiniowanej na stronie https://tahe.developpez.com/tutoriels-cours/php7.