Skip to content

14. Приклад [nuxt-11]: налаштування зображення очікування

За замовчуванням зображенням очікування для [nuxt] є смуга прогресу. Приклад [nuxt-11] показує, що його можна замінити власним зображенням очікування:

Image

Приклад [nuxt-11] також демонструє, як обробляти помилки завантаження.

Image

Приклад [nuxt-11] спочатку отримано шляхом копіювання прикладу [nuxt-10]:

Image

У [1] ми додамо плагін для клієнта, завданням якого буде обробка подій між компонентами.

14.1. Плагін [event-bus]

Плагін [event-bus] виконуватиметься як на клієнті, так і на сервері, але ми побачимо, що на стороні сервера він не працює. Його код такий:


// створення шини подій між поданнями
import Vue from 'vue'
export default (context, inject) => {
  // канал подій
  const eventBus = new Vue()
  // введення функції [eventBus] у контекст
  inject('eventBus', () => eventBus)
}
  • рядок 5: шина подій є екземпляром класу [Vue]. Адже цей клас має методи для управління подіями:
    • [$emit]: для генерації події;
    • [$on]: для підключення до прийому певної події;

Ця шина подій оброблятиме лише одну подію, [loading], яка використовуватиметься сторінками для запуску/зупинки анімації очікування завершення асинхронної функції;

  • рядок 7: створюється функція [$eventBus] (1-й аргумент), завданням якої буде повернути щойно створений об’єкт [eventBus] (2-й аргумент). Ця функція вбудовується в контекст, щоб вона була доступною в об’єктах [context.app] та [this] сторінок;

14.2. Макет [default.vue]

Макет [default.vue] змінюється наступним чином:


<template>
  <div class="container">
    <b-card>
      <!-- повідомлення -->
      <b-alert show variant="success" align="center">
        <h4>[nuxt-11] : personnalisation de l'attente, gestion des erreurs</h4>
      </b-alert>
      <!-- поточний вигляд маршрутизації -->
      <nuxt />
      <!-- завантаження -->
      <b-alert v-if="showLoading" show variant="light">
        <strong>Requête au serveur de données en cours...</strong>
        <div class="spinner-border ml-auto" role="status" aria-hidden="true"></div>
      </b-alert>
      <!-- помилка завантаження -->
      <b-alert v-if="showErrorLoading" show variant="danger">
        <strong>La requête au serveur de données a échoué : {{ errorLoadingMessage }}</strong>
      </b-alert>
    </b-card>
  </div>
</template>

<script>
/* eslint-disable no-console */
export default {
  name: 'App',
  data() {
    return {
      showLoading: false,
      showErrorLoading: false
    }
  },
  // життєвий цикл
  beforeCreate() {
    console.log('[default beforeCreate]')
  },
  created() {
    console.log('[default created]')
    // обслуговуємо подію [loading]
    this.$eventBus().$on('loading', this.mShowLoading)
    // а також подію [errorLoadingMessage]
    this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
  },
  beforeMount() {
    console.log('[default beforeMount]')
  },
  mounted() {
    console.log('[default mounted]')
  },
  methods: {
    // управління завантаженням
    mShowLoading(value) {
      console.log('[default mShowLoading], showLoading=', value)
      this.showLoading = value
    },
    // помилка завантаження
    mShowErrorLoading(value, errorLoadingMessage) {
      console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
      this.showErrorLoading = value
      this.errorLoadingMessage = errorLoadingMessage
    }
  }
}
</script>
  • рядки 11–14: анімація очікування. Вона відображається лише в тому випадку, якщо властивість [showLoading] має значення «true» (рядок 29);
  • рядки 16–18: повідомлення про помилку завантаження. Воно відображається лише тоді, коли властивість [showErrorLoading] (рядок 30) має значення «true»;
  • рядки 29–30: під час початкового завантаження компонента анімація очікування прихована, так само як і повідомлення про помилку;
  • рядки 37–43: після створення сторінка очікує на подію [loading] (1-й аргумент) на шині подій, створеній плагіном. Після її отримання вона запускає метод [mShowLoading] із рядків 52–55 (2-й аргумент);
  • рядки 52–55: значення, отримане методом [mShowLoading], буде логічним значенням true / false. Воно використовується для показу / приховування повідомлення про очікування;
  • рядки 41–42: під час створення сторінка очікує на подію [errorLoading] (1-й аргумент) на шині подій, створеній плагіном. Після її отримання вона запускає метод [mShowErrorLoading] із рядків 57–61 (2-й аргумент);
  • рядок 57: метод [mShowErrorLoading] отримує два аргументи:
    • перший аргумент — логічне значення true/false для показу/приховування повідомлення про помилку;
    • другий аргумент присутній лише у разі виникнення помилки. Він представляє повідомлення про помилку, яке потрібно відобразити;
  • записи в журналах у рядках 53 і 58 покажуть нам, що методи [showLoading] та [showErrorLoading] не виконуються на стороні сервера;

14.3. Сторінка [page1]

Код сторінки [page1] змінюється наступним чином:


<!-- вид № 1 -->
<template>
  <Layout :left="true" :right="true">
    <!-- навігація -->
    <Navigation slot="left" />
    <!-- повідомлення-->
    <b-alert slot="right" show variant="primary"> Page 1 -- result={{ result }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page1',
  // використані компоненти
  components: {
    Layout,
    Navigation
  },
  // асинхронні дані
  asyncData(context) {
    // журнал
    console.log('[page1 asyncData started]')
    // початок очікування
    context.app.$eventBus().$emit('loading', true)
    // помилок немає
    context.app.$eventBus().$emit('errorLoading', false)
    // повертається обіцянка
    return new Promise(function(resolve, reject) {
      // імітація асинхронної функції
      setTimeout(function() {
        // кінець очікування
        context.app.$eventBus().$emit('loading', false)
        // журнал
        console.log('[page1 asyncData finished]')
        // повертаємо асинхронний результат — тут випадкове число
        resolve({ result: Math.floor(Math.random() * Math.floor(100)) })
      }, 5000)
    })
  },

  // життєвий цикл
  beforeCreate() {
    console.log('[page1 beforeCreate]')
  },
  created() {
    console.log('[page1 created]')
  },
  beforeMount() {
    console.log('[page1 beforeMount]')
  },
  mounted() {
    console.log('[page1 mounted]')
  }
}
</script>
  • зміни відбуваються у функції [asyncData] у рядках 26–47;
  • рядки 29–30: перед початком роботи асинхронної функції надсилається подія [loading] іншим сторінкам додатка. Нагадаємо, що у функції [asyncData] немає доступу до об’єкта [this], який ще не створено. Тому використовується контекст, який отримує як аргумент функція [asyncData] (рядок 26);
  • рядок 30: використовується шина подій для повідомлення про початок завантаження;
  • рядок 38: використовується шина подій для повідомлення про завершення завантаження;

Примітка: під час виконання, коли сторінка [page1] запитується на сервері, зображення очікування не відображається. У логах видно, що на стороні сервера метод [default.mShowLoading] не викликається. У будь-якому разі, відображення зображення очікування не має сенсу під час запиту сторінки до сервера. Сервер надсилає сторінку до браузера клієнта лише після завершення функції [asyncData]. Тоді зображення очікування стає непотрібним. Це стосується всіх сторінок додатка, які запитуються безпосередньо до сервера.

14.4. Сторінка [index]

Код сторінки [index] такий:


<!-- головна сторінка -->
<template>
  <Layout :left="true" :right="true">
    <!-- навігація -->
    <Navigation slot="left" />
    <!-- повідомлення-->
    <b-alert slot="right" show variant="warning">
      Home
    </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable nuxt/no-env-in-hooks */
/* eslint-disable nuxt/no-timing-in-fetch-data */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Home',
  // використані компоненти
  components: {
    Layout,
    Navigation
  },
  // асинхронні дані
  asyncData(context) {
    // журнал
    console.log('[page1 asyncData started]')
    // початок очікування
    context.app.$eventBus().$emit('loading', true)
    // помилок немає
    context.app.$eventBus().$emit('errorLoading', false)
    // повертається обіцянка
    return new Promise(function(resolve, reject) {
      // імітація асинхронної функції
      setTimeout(function() {
        // кінець очікування
        context.app.$eventBus().$emit('loading', false)
        // запис у журнал
        console.log('[page1 asyncData finished]')
        // повертається помилка
        reject(new Error("le serveur n'a pas répondu assez vite"))
      }, 5000)
    }).catch((e) => context.error({ statusCode: 500, message: e.message }))
  },
  // життєвий цикл
  beforeCreate() {
    console.log('[home beforeCreate]')
  },
  created() {
    console.log('[home created]')
  },
  beforeMount() {
    console.log('[home beforeMount]')
  },
  mounted() {
    console.log('[home mounted]')
    // помилки немає
    this.$eventBus().$emit('errorLoading', false)
  }
}
</script>
  • рядки 30–49: функція [asyncData] ідентична функції на сторінці [page1], за винятком однієї деталі: у рядку 46 асинхронна функція завершується у разі невдачі (використання методу [reject]);
  • рядок 46: параметром функції [reject] є екземпляр класу [Error]. Параметром конструктора [Error] є повідомлення про помилку;
  • рядок 48: ця помилка перехоплюється методом [catch] класу [Promise], який отримує помилку як параметр. Потім використовується функція [context.error] для оголошення помилки. Параметром функції [context.error] є об’єкт, який у даному випадку має дві властивості:
    • [statusCode]: код помилки HTTP;
    • [message]: повідомлення про помилку;

Незалежно від того, чи функція [asyncData] виконується клієнтом чи сервером, у разі помилки [context.error] функція [nuxt] відображає сторінку [layouts / error.vue]:

Image

Хоча це сторінка, сторінка [error.vue] шукається у папці [layouts] (можливо, щоб уникнути її включення до маршрутів додатка?). Тут сторінка [error.vue] виглядає так:


<!-- визначення HTML подання -->
<template>
  <!-- форматування -->
  <Layout :left="true" :right="true">
    <!-- сповіщення у правій колонці -->
    <template slot="right">
      <!-- повідомлення на жовтому тлі -->
      <b-alert show variant="danger" align="center">
        <h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
      </b-alert>
    </template>
    <!-- меню навігації у лівій колонці -->
    <Navigation slot="left" />
  </Layout>
</template>

<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable nuxt/no-env-in-hooks */

import Layout from '@/components/layout'
import Navigation from '@/components/navigation'

export default {
  name: 'Error',
  // використані компоненти
  components: {
    Layout,
    Navigation
  },
  // властивість [props]
  props: { error: { type: Object, default: () => 'waiting ...' } },
  // життєвий цикл
  beforeCreate() {
    // клієнт і сервер
    console.log('[error beforeCreate]')
  },
  created() {
    // клієнт і сервер
    console.log('[error created, error=]', this.error)
  },
  beforeMount() {
    // тільки клієнт
    console.log('[error beforeMount]')
  },
  mounted() {
    // тільки клієнт
    console.log('[error mounted]')
  }
}
</script>

Коли [nuxt] відображає сторінку [error.vue], вона передає їй у властивість [props] інформацію про помилку, що сталася (рядок 33). Якщо помилку спричинив [context.error(objet1)], властивість [props] сторінки [error.vue] матиме значення [objet1]. У документації [nuxt] зазначено, що [objet1] має мати принаймні атрибути [statusCode, message]. У рядку 9 відображається рядок jSON отриманого об’єкта [objet1].

14.5. Сторінка [page2]

На сторінці [page2] показано інший спосіб обробки помилки:

  • у [page1] помилка відображається на окремій сторінці [error.vue];
  • у [page2] помилка відображатиметься на сторінці [page2], яка спричинила помилку;

Код [page2] такий:


<!-- вид № 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- навігація -->
    <Navigation slot="left" />
    <!-- повідомлення -->
    <b-alert slot="right" show variant="secondary">
      Page 2
    </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page2',
  // використані компоненти
  components: {
    Layout,
    Navigation
  },
  // асинхронні дані
  asyncData(context) {
    // журнал
    console.log('[page2 asyncData started]')
    // початок очікування
    context.app.$eventBus().$emit('loading', true)
    // помилок немає
    context.app.$eventBus().$emit('errorLoading', false)
    // повертається обіцянка
    return new Promise(function(resolve, reject) {
      // імітація асинхронної функції
      setTimeout(function() {
        // кінець очікування
        context.app.$eventBus().$emit('loading', false)
        // довільно генерується помилка
        const errorLoadingMessage = "le serveur n'a pas répondu assez vite"
        // успішне завершення
        resolve({ showErrorLoading: true, errorLoadingMessage })
        // запис у журнал
        console.log('[page2 asyncData finished]')
      }, 5000)
    })
  },
  // життєвий цикл
  beforeCreate() {
    console.log('[page2 beforeCreate]')
  },
  created() {
    console.log('[page2 created]')
  },
  beforeMount() {
    console.log('[page2 beforeMount]')
  },
  mounted() {
    console.log('[page2 mounted]')
    // клієнт
    if (this.showErrorLoading) {
      console.log('[page2 mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
  }
}
</script>

Знову вставляємо функцію [asyncData] у код сторінки, і, як і [index], [page2] згенерує помилку, яку цього разу ми обробимо по-іншому.

  • рядок 44: і сервер, і клієнт успішно завершують обіцянку, повертаючи результат [{ showErrorLoading: true, errorLoadingMessage }]. Ми знаємо, що це призведе до включення властивостей [showerrorLoading, errorLoadingMessage] у властивості [data] сторінки і що клієнт отримає ці властивості;
  • рядки 60–67: відомо, що функція [mounted] виконується лише клієнтом;
  • рядок 63: клієнт перевіряє, чи було встановлено властивість [showErrorLoading] (сервером або клієнтом, залежно від обставин). Якщо так, він генерує подію [‘errorLoading’] (рядок 65), щоб сторінка [default] відобразила повідомлення про помилку [this.errorLoadingMessage]. У підсумку сервер надсилає сторінку без відображеного повідомлення про помилку. Воно відображається в останній момент клієнтом під час «завантаження» сторінки;

14.6. Exécution

14.6.1. [nuxt.config]

Файл виконання [nuxt.config.js] має такий вигляд:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    title: 'Introduction à [nuxt.js]',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: 'ssr routing loading asyncdata middleware plugins store'
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },
  /*
   ** Customize the progress-bar color
   */
  loading: false,

  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [{ src: '@/plugins/event-bus' }],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // Документація: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Документація: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Документація: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  },
  // каталог з вихідним кодом
  srcDir: 'nuxt-11',
  // маршрутизатор
  router: {
    // кореневий каталог додатка URL
    base: '/nuxt-11/'
  },
  // сервер
  server: {
    // порт служби, за замовчуванням 3000
    port: 81,
    // мережеві адреси, на яких відбувається прослуховування; за замовчуванням localhost: 127.0.0.1
    // 0.0.0.0 = усі мережеві адреси комп’ютера
    host: 'localhost'
  }
}
  • рядок 22: встановлюємо властивість [loading] на [false], щоб [nuxt] не використовував своє зображення очікування за замовчуванням;
  • рядок 31: плагін, що визначає шину подій;

14.6.2. L — сторінка [index], що виконується сервером

Запросимо сторінку [index] у сервера (вводимо URL [http://localhost:81/nuxt-11/] вручну). Сторінка, що відображається браузером клієнта, виглядає так:

Image

Журнали мають такий вигляд:

Image

  • у записі [3] видно, що сервер надсилає сторінку [error.vue];
  • у записі [4] видно, що клієнт також відображає сторінку [error] з тією самою помилкою, що й сервер;
  • можна помітити, що метод [mShowLoading] сторінки [default] не був викликаний на стороні сервера, хоча сторінка [index] активувала очікування. Цей метод викликається при отриманні події, і, очевидно, обробка подій не реалізована на стороні сервера;

Розглянемо вихідний код сторінки, отриманої клієнтським браузером:


<!doctype html>
<html data-n-head-ssr>
<head>
  <title>Introduction à [nuxt.js]</title>
  <meta data-n-head="ssr" charset="utf-8">
  <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
  <meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
  <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
  <base href="/nuxt-11/">
  <link rel="preload" href="/nuxt-11/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-11/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-11/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-11/_nuxt/app.js" as="script">
  ...
</head>
<body>
  <div data-server-rendered="true" id="__nuxt">
    <div id="__layout">
      <div class="container">
        <div class="card">
          <div class="card-body">
            <div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
            <h4>[nuxt-11] : personnalisation de l'attente, gestion des erreurs</h4>
              </div>
            <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-11/" target="_self" class="nav-link active nuxt-link-active">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-11/page1" target="_self" class="nav-link">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-11/page2" target="_self" class="nav-link">
                        Page 2
                      </a>
                    </li>
                  </ul>
                </div> <div class="col-10"><div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-danger">
                <h4>L'erreur suivante s'est produite : {&quot;statusCode&quot;:500,&quot;message&quot;:&quot;le serveur n'a pas répondu assez vite&quot;}</h4>
                    </div>
                </div>
              </div>
            </div>  
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>window.__NUXT__ = (function (a, b, c, d) {
  d.statusCode = 500; d.message = "le serveur n'a pas répondu assez vite";
  return {
    layout: "default", data: [d], error: d, serverRendered: true,
    logs: [
      { date: new Date(1575047424168), args: ["[event-bus créé]"], type: a, level: b, tag: c },
      { date: new Date(1575047424175), args: ["[page1 asyncData started]"], type: a, level: b, tag: c },
      { date: new Date(1575047429455), args: ["[page1 asyncData finished]"], type: a, level: b, tag: c },
      { date: new Date(1575047429515), args: ["[default beforeCreate]"], type: a, level: b, tag: c },
      { date: new Date(1575047429675), args: ["[default created]"], type: a, level: b, tag: c },
      { date: new Date(1575047430157), args: ["[error beforeCreate]"], type: a, level: b, tag: c },
      { date: new Date(1575047430246), args: ["[error created, error=]", "{ statusCode: 500,\n  message: 'le serveur n\\'a pas répondu assez vite' }"], type: a, level: b, tag: c }]
  }
    }("log", 2, "", {}));</script>
  <script src="/nuxt-11/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-11/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-11/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-11/_nuxt/app.js" defer></script>
</body>
</html>
  • рядок 57: бачимо, що сервер надіслав об’єкт [d], який відображає помилку, що сталася на стороні сервера;
  • рядок 59: бачимо властивість [error], значенням якої є об’єкт [d]. Можна припустити, що саме наявність властивості [error] на сторінці, надісланій сервером, змушує клієнтські скрипти відображати сторінку [error.vue] з помилкою [error];

14.6.3. Сторінка [page1], виконана сервером

Вводимо вручну URL [http://localhost:81/nuxt-11/page1]. Через 5 секунд браузер відображає таку сторінку:

Image

Відображаються такі записи журналу:

Image

  • у [1] — журнали сервера. Можна помітити, що метод [mShowLoading] сторінки [default] не був викликаний;
  • у [2] — журнали клієнта;

14.6.4. Сторінка [page2], виконана сервером

Ми вручну вводимо URL [http://localhost:81/nuxt-11/page2]. Через 5 секунд браузер відображає таку сторінку:

Image

Розглянемо журнали, що відображаються в браузері:

Image

  • у [1] — журнали сервера. Нагадаємо, що сервер вставив атрибути [showErrorLoading, errorLoadingMessage] у сторінку, надіслану клієнтському браузеру. Ми знаємо, що ці атрибути будуть інтегровані в [data] сторінки, яка відображається клієнтом
  • у [3], коли сторінка [page2] завантажується, вона виявляє, що властивість [showErrorLoading] має значення «true». Тоді вона надсилає подію на сторінку [default], щоб та відобразила повідомлення про помилку, надіслане сервером [4];

14.6.5. Сторінка [index], що виконується клієнтом

Тепер використовуємо навігаційні посилання для відображення трьох сторінок. Усі сторінки, що відображаються клієнтом, ідентичні тим, що відображаються сервером. Єдина відмінність полягає в тому, що щоразу відображається зображення очікування закінчення 5 секунд.

Почнемо зі сторінки [index]. Тоді відображається зображення очікування:

Image

а через 5 секунд з’являється наступна сторінка:

Image

Отже, кінцева сторінка є ідентичною тій, що отримується на стороні сервера.

Image

Нагадаємо функцію [asyncData] зі сторінки [index]:


asyncData(context) {
    // журнал
    console.log('[page1 asyncData started]')
    // початок очікування
    context.app.$eventBus().$emit('loading', true)
    // помилок немає
    context.app.$eventBus().$emit('errorLoading', false)
    // повертається обіцянка
    return new Promise(function(resolve, reject) {
      // імітація асинхронної функції
      setTimeout(function() {
        // кінець очікування
        context.app.$eventBus().$emit('loading', false)
        // запис у журнал
        console.log('[page1 asyncData finished]')
        // повертається помилка
        reject(new Error("le serveur n'a pas répondu assez vite"))
      }, 5000)
    }).catch((e) => context.error({ statusCode: 500, message: e.message }))
}

Журнали клієнта мають такий вигляд:

Image

  • у [1] запускається функція [asyncData];
  • у [2] запускається зображення очікування;
  • у [2-3] видно, що сторінка [default] отримала події [loading, true], [2] та [errorLoading, false], надіслані функцією [asyncData] зі сторінки [index] (рядки 5 та 7);
  • у [4] — кінець очікування. Сторінка [default] отримала подію [loading, false], надіслану сторінкою [index] (рядок 13);
  • на [5] функція [asyncData] завершила свою роботу;
  • оскільки функція [asyncData] спричинила помилку з [context.error] (рядок 19), сторінка [error] відображається як [6];

14.6.6. Сторінка [page1], що виконується клієнтом

Після 5 секунд очікування клієнт відображає наступну сторінку:

Image

Нагадаємо код функції [asyncData] з [page1]:


asyncData(context) {
    // запис у журнал
    console.log('[page1 asyncData started]')
    // початок очікування
    context.app.$eventBus().$emit('loading', true)
    // помилки немає
    context.app.$eventBus().$emit('errorLoading', false)
    // повертається обіцянка
    return new Promise(function(resolve, reject) {
      // імітація асинхронної функції
      setTimeout(function() {
        // кінець очікування
        context.app.$eventBus().$emit('loading', false)
        // журнал
        console.log('[page1 asyncData finished]')
        // повертаємо асинхронний результат — тут випадкове число
        resolve({ result: Math.floor(Math.random() * Math.floor(100)) })
      }, 5000)
    })
},

Журнали мають такий вигляд:

Image

14.6.7. Сторінка [page2], що виконується клієнтом

Після 5 секунд очікування клієнт відображає таку сторінку:

Image

Нагадаємо код функцій [asyncData] та [mounted] з [page2]:


asyncData(context) {
    // журнал
    console.log('[page2 asyncData started]')
    // початок очікування
    context.app.$eventBus().$emit('loading', true)
    // помилок немає
    context.app.$eventBus().$emit('errorLoading', false)
    // повертаємо обіцянку
    return new Promise(function(resolve, reject) {
      // імітуємо асинхронну функцію
      setTimeout(function() {
        // кінець очікування
        context.app.$eventBus().$emit('loading', false)
        // довільно генерується помилка
        const errorLoadingMessage = "le serveur n'a pas répondu assez vite"
        // успішне завершення
        resolve({ showErrorLoading: true, errorLoadingMessage })
        // запис у журнал
        console.log('[page2 asyncData finished]')
      }, 5000)
    })
  }

mounted() {
    console.log('[page2 mounted]')
    // клієнт
    if (this.showErrorLoading) {
      console.log('[page2 mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
}

Журнали мають такий вигляд:

Image

  • у [1] сторінка [default] отримала подію [showErrorLoading, true], надіслану [page2] (рядок 29), яка вимагає від неї відобразити повідомлення про помилку;