12. Проект [vuejs-10]: плагін [dao], асинхронні запити HTTP
Структура проекту [vuejs-10] така:

Проєкт [vuejs-10] демонструє компонент, який надсилає запит HTTP на віддалений сервер. Використовується така архітектура:

Компонент [Vue.js] використовує рівень [dao] для взаємодії з сервером розрахунку податку.
12.1. Встановлення залежностей
Додаток [vuejs-10] використовує бібліотеку [axios] для здійснення асинхронних запитів до сервера розрахунку податків. Нам потрібно встановити цю залежність:

- у [4-5] рядок, доданий до файлу [package.json] після встановлення бібліотеки [axios] [1-3] ;
12.2. Клас [Dao]
Клас [Dao] — це той, що був розроблений у параграфі [La classe Dao]. Наводимо його тут для довідки:
'use strict';
// імпорт
import qs from 'qs'
// клас [Dao]
class Dao {
// конструктор
constructor(axios) {
this.axios = axios;
// сесійний файл cookie
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// ініціалізація сесії
async initSession() {
// параметри запиту HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// параметри URL
params: {
action: 'init-session',
type: 'json'
}
};
// виконання запиту HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// параметри запиту HHTP [post /main.php?action=authentifier-utilisateur]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// тіло POST
data: qs.stringify({
user: user,
password: password
}),
// параметри URL
params: {
action: 'authentifier-utilisateur'
}
};
// виконання запиту HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// параметри запиту HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// параметри запиту URL
params: {
action: 'get-admindata'
}
};
// виконання запиту HTTP
const data = await this.getRemoteData(options);
// результат
return data;
}
async getRemoteData(options) {
// для сесійного файлу cookie
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// виконання запиту HTTP
let response;
try {
// асинхронний запит
response = await this.axios.request('main.php', options);
} catch (error) {
// параметр [error] є екземпляром винятку — він може мати різні форми
if (error.response) {
// відповідь сервера міститься в [error.response]
response = error.response;
} else {
// помилка повторюється
throw error;
}
}
// response — це повна відповідь HTTP від сервера (заголовки HTTP + сама відповідь)
// отримуємо сесійний файл cookie, якщо він існує
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie — це масив
// у цьому масиві шукається сесійний файл cookie
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// шукаємо сесійний файл cookie
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// зберігаємо сесійний файл cookie
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// знайдено
trouvé = true;
} else {
// наступний елемент
i++;
}
}
}
// відповідь сервера знаходиться в [response.data]
return response.data;
}
}
// експорт класу
export default Dao;
Проект [vuejs-10] використовує лише асинхронний метод [initSession] із рядків 18–30. Нагадаємо, що клас [Dao] інстанціюється з параметром [axios] (рядок 10), який ініціалізується кодом, що викликає. Цим кодом, що викликає, тут буде скрипт [./main.js].
12.3. Плагін [pluginDao]
Плагін [pluginDao] має такий вигляд:
export default {
install(Vue, dao) {
// додає властивість [$dao] до класу Vue
Object.defineProperty(Vue.prototype, '$dao', {
// коли відбувається звернення до Vue.$dao, повертається другий параметр [dao]
get: () => dao,
})
}
}
Якщо згадати пояснення, наведене для плагіна [event-bus], то можна побачити, що плагін [pluginDao] створює у класі / функції [Vue] нову властивість під назвою [$dao]. Цей властивість матиме (це ще належить довести) за значення об’єкт, експортований скриптом [./Dao], тобто попередній клас [Dao].
12.4. Головний скрипт [main.js]
Код головного скрипта [main.js] такий:
// імпортує
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios';
// плагіни
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);
// bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
// шар [dao]
import Dao from './Dao';
// конфігурація axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
axios.defaults.withCredentials = true;
// інстанціювання шару [dao]
const dao = new Dao(axios);
// плагін [dao]
import pluginDao from './plugins/dao'
Vue.use(pluginDao, dao)
// конфігурація
Vue.config.productionTip = false
// інстанціювання проєкту [App]
new Vue({
render: h => h(App),
}).$mount('#додаток')
Скрипт [main.js]:
- ініціює шар [dao] у рядках 14–21;
- інтегрує плагін [pluginDao] у рядках 24–25;
- у рядку 15: імпортується клас [Dao];
- рядки 17–18: налаштовується об’єкт [axios], який виконує запити HTTP. Цей об’єкт імпортується у рядку 4;
- рядок 17: визначення [timeout] тривалістю 2 секунди;
- рядок 18: URL сервера розрахунку податку;
- рядок 19: для обміну файлами cookie з сервером;
- рядки 24–25: використання плагіна [pluginDao]
- рядок 24: імпорт плагіна;
- рядок 25: інтеграція плагіна. Бачимо, що другим параметром методу [Vue.use] є посилання на шар [dao], визначений у рядку 21. Саме з цієї причини властивість [Vue.$dao] буде вказувати на шар [dao] у всіх екземплярах класу / функції [Vue], тобто у всіх компонентах [Vue.js];
12.5. Головний вигляд [App.vue]
Код головного виду [App] має такий вигляд:
<template>
<div class="container">
<b-card>
<!-- повідомлення -->
<b-alert show variant="success" align="center">
<h4>[vuejs-10] : plugin [dao], requêtes HTTP asynchrones</h4>
</b-alert>
<!-- компонент, що виконує асинхронний запит до сервера розрахунку податків-->
<Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
<!-- відображення можливої помилки -->
<b-alert show
variant="danger"
v-if="showError">Evénement [error] intercepté par [App]. Valeur reçue = {{error}}</b-alert>
<!-- повідомлення про очікування зі спінером -->
<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",
// стан компонента
data() {
return {
// керує індикатором очікування
showWaiting: false,
// керує відображенням помилки
showError: false,
// перехоплена помилка
error: {}
};
},
// використані компоненти
components: {
Component1
},
// методи обробки подій
methods: {
// початок очікування
beginWaiting() {
// виведення повідомлення про очікування
this.showWaiting = true;
// приховування повідомлення про помилку
this.showError = false;
},
// завершення очікування
endWaiting() {
// приховування очікування
this.showWaiting = false;
},
// обробка помилки
doSomethingWithError(error) {
// фіксується, що сталася помилка
this.error = error;
// виводиться повідомлення про помилку
this.showError = true;
}
}
};
</script>
Коментарі
- рядок 9: [Component1] — це компонент, який виконує асинхронний запит HTTP. Він може генерувати три події:
- [beginWaiting]: запит буде виконано. Необхідно відобразити повідомлення про очікування для користувача;
- [endWaiting]: запит завершено. Очікування слід припинити;
- [error]: запит завершився з помилкою. Необхідно відобразити повідомлення про помилку;
- рядки 10–13: сповіщення, яке відображає можливе повідомлення про помилку. Воно керується булевим значенням [showError] у рядку 33. Воно відображає помилку з рядка 35;
- рядки 14–18: сповіщення, яке відображає повідомлення про очікування зі спінером. Воно керується булевою змінною [showWaiting] у рядку 47;
- рядки 45–50: [beginWaiting] — це метод, що виконується при отриманні події [beginWaiting]. Він відображає повідомлення про очікування (рядок 47) і приховує повідомлення про помилку (рядок 49) на випадок, якщо воно залишилося видимим після попередньої операції;
- рядки 52–55: [endWaiting] — це метод, що виконується при отриманні події [endWaiting]. Він приховує повідомлення про очікування (рядок 54);
- рядки 57–62: [doSomethingWithError] — це метод, що виконується при отриманні події [error]. Він реєструє отриману помилку (рядок 59) і відображає повідомлення про помилку (рядок 61);
12.6. Компонент [Component1]
Код компонента [Component1] такий:
<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",
// стан компонента
data() {
return {
showMsg: false
};
},
// методи обробки подій
methods: {
// обробка даних, отриманих від сервера
doSomethingWithData(data) {
// здійснюється запис отриманих даних
this.data = data;
// виводимо їх на екран
this.showMsg = true;
}
},
// компонент щойно створено
created() {
// ініціалізується сесія з сервером — асинхронний запит
// використовується обіцянка, повернута методами шару [dao]
// повідомляється про початок операції
this.$emit("beginWaiting");
// запускається асинхронна операція
this.$dao
// необхідно ініціалізувати сесію jSON із сервером розрахунку податку
.initSession()
// метод, який обробляє отримані дані у разі успіху
.then(data => {
// обробка отриманих даних
this.doSomethingWithData(data);
})
// метод, який обробляє помилку у разі її виникнення
.catch(error => {
// повідомлення про помилку передається батьківському компоненту
this.$emit("error", error.message);
}).finally(() => {
// завершення очікування
this.$emit("endWaiting");
})
}
};
</script>
Коментарі
- рядки 4–6: компонент складається з єдиного сповіщення, яке відображає значення, повернуте сервером розрахунку податку, але лише у разі успішного виконання запиту HTTP. Це сповіщення керується булевим значенням [showMsg] у рядку 17;
- рядки 31–53: запит HTTP виконується одразу після створення компонента. Тому його код розміщується в методі [created] у рядку 31;
- рядок 35: батьківському компоненту повідомляється, що асинхронний запит розпочнеться;
- рядки 37–39: виконується метод [this.$dao.initSession]. Він ініціалізує сесію jSON із сервером розрахунку податків. Негайним результатом цього методу є [Promise];
- рядки 41–44: цей код виконується, коли сервер повернув результат без помилок. Результат сервера міститься в [data]. У рядку 43 метод [doSomethingWithData] отримує завдання обробити цей результат;
- рядки 46–49: цей код виконується у разі помилки під час виконання запиту. У рядку 48 батьківському компоненту повідомляється про виникнення помилки та передається повідомлення про помилку [error.message];
- рядки 49–52: цей код виконується в будь-якому випадку. Батьківському компоненту повідомляється, що запит HTTP завершено;
- рядки 23–28: метод [doSomethingWithData] призначений для обробки даних [data], надісланих сервером. У рядку 25 ці дані записуються, а в рядку 27 — відображаються;
12.7. Виконання проекту

Якщо під час запуску проекту сервер розрахунку податків не запущений, то отримуємо такий результат:

Запустимо сервер [Laragon] (див. https://tahe.developpez.com/tutoriels-cours/php7) і перезавантажимо сторінку, наведену вище. Тоді результат буде таким:

Примітка: тут ми використовуємо версію 14 сервера розрахунку податків, визначену на https://tahe.developpez.com/tutoriels-cours/php7.