Skip to content

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

Структура проекту [vuejs-10] така:

Image

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

Image

Компонент [Vue.js] використовує рівень [dao] для взаємодії з сервером розрахунку податку.

12.1. Встановлення залежностей

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

Image

  • у [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. Виконання проекту

Image

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

Image

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

Image

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