Skip to content

14. Przykład [nuxt-11]: dostosowanie obrazu oczekiwania

Domyślnie obrazem oczekiwania dla funkcji [nuxt] jest pasek postępu. Przykład [nuxt-11] pokazuje, że można go zastąpić własnym obrazem oczekiwania:

Image

Przykład [nuxt-11] pokazuje również, jak radzić sobie z błędami ładowania.

Image

Przykład [nuxt-11] uzyskano początkowo poprzez skopiowanie przykładu [nuxt-10]:

Image

W pliku [1] dodamy wtyczkę dla klienta, której zadaniem będzie obsługa zdarzeń między komponentami.

14.1. Wtyczka [event-bus]

Wtyczka [event-bus] będzie uruchamiana zarówno przez klienta, jak i serwer, ale zobaczymy, że nie działa po stronie serwera. Jej kod wygląda następująco:


// tworzy się magistralę zdarzeń między widokami
import Vue from 'vue'
export default (context, inject) => {
  // magistrala zdarzeń
  const eventBus = new Vue()
  // wstrzyknięcie funkcji [eventBus] do kontekstu
  inject('eventBus', () => eventBus)
}
  • wiersz 5: magistrala zdarzeń jest instancją klasy [Vue]. Klasa ta posiada bowiem metody do zarządzania zdarzeniami:
    • [$emit]: do wygenerowania zdarzenia;
    • [$on]: do nasłuchiwania konkretnego zdarzenia;

Ta magistrala zdarzeń będzie obsługiwać tylko jedno zdarzenie, [loading], które będzie wykorzystywane przez strony do uruchamiania/zatrzymywania animacji oczekiwania na zakończenie funkcji asynchronicznej;

  • wiersz 7: tworzymy funkcję [$eventBus] (pierwszy argument), której zadaniem będzie zwracanie właśnie utworzonego obiektu [eventBus] (drugi argument). Funkcja ta jest wstrzykiwana do kontekstu, aby była dostępna w obiektach [context.app] i [this] stron;

14.2. Układ [default.vue]

Układ [default.vue] zmienia się w następujący sposób:


<template>
  <div class="container">
    <b-card>
      <!-- komunikat -->
      <b-alert show variant="success" align="center">
        <h4>[nuxt-11] : personnalisation de l'attente, gestion des erreurs</h4>
      </b-alert>
      <!-- bieżący widok routingu -->
      <nuxt />
      <!-- ładowanie -->
      <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łąd ładowania -->
      <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
    }
  },
  // cykl życia
  beforeCreate() {
    console.log('[default beforeCreate]')
  },
  created() {
    console.log('[default created]')
    // obsługujemy zdarzenie [loading]
    this.$eventBus().$on('loading', this.mShowLoading)
    // a także zdarzenie [errorLoadingMessage]
    this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
  },
  beforeMount() {
    console.log('[default beforeMount]')
  },
  mounted() {
    console.log('[default mounted]')
  },
  methods: {
    // zarządzanie ładowaniem
    mShowLoading(value) {
      console.log('[default mShowLoading], showLoading=', value)
      this.showLoading = value
    },
    // błąd ładowania
    mShowErrorLoading(value, errorLoadingMessage) {
      console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
      this.showErrorLoading = value
      this.errorLoadingMessage = errorLoadingMessage
    }
  }
}
</script>
  • wiersze 11–14: animacja oczekiwania. Jest wyświetlana tylko wtedy, gdy właściwość [showLoading] ma wartość true (wiersz 29);
  • wiersze 16–18: komunikat o błędzie ładowania. Jest on wyświetlany tylko wtedy, gdy właściwość [showErrorLoading] (wiersz 30) ma wartość prawdziwą;
  • wiersze 29–30: podczas pierwszego ładowania komponentu animacja oczekiwania jest ukryta, podobnie jak komunikat o błędzie;
  • wiersze 37–43: po utworzeniu strona nasłuchuje zdarzenia [loading] (pierwszy argument) na magistrali zdarzeń utworzonej przez wtyczkę. Po jego odebraniu uruchamia metodę [mShowLoading] z wierszy 52–55 (drugi argument);
  • wiersze 52–55: wartość otrzymana przez metodę [mShowLoading] będzie wartością logiczną true/false. Służy ona do wyświetlania/ukrywania komunikatu o oczekiwaniu;
  • wiersze 41–42: po utworzeniu strona nasłuchuje zdarzenia [errorLoading] (pierwszy argument) na magistrali zdarzeń utworzonej przez wtyczkę. Po jego odebraniu uruchamia metodę [mShowErrorLoading] z wierszy 57–61 (drugi argument);
  • wiersz 57: metoda [mShowErrorLoading] przyjmuje dwa argumenty:
    • pierwszy argument to wartość logiczna true/false służąca do wyświetlenia lub ukrycia komunikatu o błędzie;
    • drugi argument występuje tylko w przypadku wystąpienia błędu. Reprezentuje on komunikat o błędzie, który ma zostać wyświetlony;
  • zapisy w wierszach 53 i 58 pokazują, że metody [showLoading] i [showErrorLoading] nie są wykonywane po stronie serwera;

14.3. Strona [page1]

Kod strony [page1] zmienia się w następujący sposób:


<!-- widok nr 1 -->
<template>
  <Layout :left="true" :right="true">
    <!-- nawigacja -->
    <Navigation slot="left" />
    <!-- komunikat-->
    <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',
  // używane komponenty
  components: {
    Layout,
    Navigation
  },
  // dane asynchroniczne
  asyncData(context) {
    // log
    console.log('[page1 asyncData started]')
    // początek oczekiwania
    context.app.$eventBus().$emit('loading', true)
    // brak błędu
    context.app.$eventBus().$emit('errorLoading', false)
    // zwracana jest obietnica
    return new Promise(function(resolve, reject) {
      // symulacja funkcji asynchronicznej
      setTimeout(function() {
        // koniec oczekiwania
        context.app.$eventBus().$emit('loading', false)
        // log
        console.log('[page1 asyncData finished]')
        // zwracamy wynik asynchroniczny – w tym przypadku liczbę losową
        resolve({ result: Math.floor(Math.random() * Math.floor(100)) })
      }, 5000)
    })
  },

  // cykl życia
  beforeCreate() {
    console.log('[page1 beforeCreate]')
  },
  created() {
    console.log('[page1 created]')
  },
  beforeMount() {
    console.log('[page1 beforeMount]')
  },
  mounted() {
    console.log('[page1 mounted]')
  }
}
</script>
  • zmiany mają miejsce w funkcji [asyncData] w wierszach 26–47;
  • wiersze 29–30: przed rozpoczęciem funkcji asynchronicznej wysyłane jest zdarzenie [loading] do pozostałych stron aplikacji. Należy pamiętać, że w funkcji [asyncData] nie ma dostępu do obiektu [this], który nie został jeszcze utworzony. Wykorzystuje się zatem kontekst przekazywany jako argument funkcji [asyncData] (wiersz 26);
  • wiersz 30: wykorzystuje się magistralę zdarzeń, aby zasygnalizować rozpoczęcie ładowania;
  • wiersz 38: wykorzystuje się magistralę zdarzeń, aby zasygnalizować zakończenie ładowania;

Uwaga: Podczas wykonywania, gdy strona [page1] jest żądana od serwera, obrazek oczekiwania nie jest wyświetlany. W logach widać, że po stronie serwera metoda [default.mShowLoading] nie jest wywoływana. W każdym razie wyświetlanie obrazka oczekiwania nie ma sensu, gdy strona jest żądana od serwera. Serwer wysyła stronę do przeglądarki klienta dopiero po zakończeniu działania funkcji [asyncData]. Obrazek oczekiwania jest wówczas zbędny. Dotyczy to wszystkich stron aplikacji żądanych bezpośrednio od serwera.

14.4. Strona [index]

Kod strony [index] jest następujący:


<!-- strona główna -->
<template>
  <Layout :left="true" :right="true">
    <!-- nawigacja -->
    <Navigation slot="left" />
    <!-- komunikat-->
    <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',
  // używane komponenty
  components: {
    Layout,
    Navigation
  },
  // dane asynchroniczne
  asyncData(context) {
    // log
    console.log('[page1 asyncData started]')
    // początek oczekiwania
    context.app.$eventBus().$emit('loading', true)
    // brak błędu
    context.app.$eventBus().$emit('errorLoading', false)
    // zwracamy obietnicę
    return new Promise(function(resolve, reject) {
      // symulacja funkcji asynchronicznej
      setTimeout(function() {
        // koniec oczekiwania
        context.app.$eventBus().$emit('loading', false)
        // log
        console.log('[page1 asyncData finished]')
        // zwracamy błąd
        reject(new Error("le serveur n'a pas répondu assez vite"))
      }, 5000)
    }).catch((e) => context.error({ statusCode: 500, message: e.message }))
  },
  // cykl życia
  beforeCreate() {
    console.log('[home beforeCreate]')
  },
  created() {
    console.log('[home created]')
  },
  beforeMount() {
    console.log('[home beforeMount]')
  },
  mounted() {
    console.log('[home mounted]')
    // brak błędu
    this.$eventBus().$emit('errorLoading', false)
  }
}
</script>
  • wiersze 30–49: funkcja [asyncData] jest identyczna z funkcją na stronie [page1], z jednym wyjątkiem: w wierszu 46 funkcja asynchroniczna kończy się niepowodzeniem (zastosowanie metody [reject]);
  • wiersz 46: parametrem funkcji [reject] jest instancja klasy [Error]. Parametrem konstruktora [Error] jest komunikat o błędzie;
  • wiersz 48: błąd ten jest przechwytywany przez metodę [catch] klasy [Promise], która otrzymuje błąd jako parametr. Następnie wykorzystuje się funkcję [context.error] w celu zgłoszenia błędu. Parametrem funkcji [context.error] jest obiekt posiadający w tym przypadku dwie właściwości:
    • [statusCode]: kod błędu HTTP;
    • [message]: komunikat o błędzie;

Niezależnie od tego, czy funkcja [asyncData] jest wykonywana przez klienta czy serwer, w przypadku wystąpienia błędu [context.error] funkcja [nuxt] wyświetla stronę [layouts / error.vue]:

Image

Chociaż jest to strona, strona [error.vue] jest wyszukiwana w folderze [layouts] (być może po to, aby uniknąć jej uwzględnienia w ścieżkach aplikacji?). W tym przypadku strona [error.vue] wygląda następująco:


<!-- definicja widoku HTML -->
<template>
  <!-- układ strony -->
  <Layout :left="true" :right="true">
    <!-- alert w prawej kolumnie -->
    <template slot="right">
      <!-- komunikat na żółtym tle -->
      <b-alert show variant="danger" align="center">
        <h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
      </b-alert>
    </template>
    <!-- menu nawigacyjne w lewej kolumnie -->
    <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',
  // używane komponenty
  components: {
    Layout,
    Navigation
  },
  // właściwość [props]
  props: { error: { type: Object, default: () => 'waiting ...' } },
  // cykl życia
  beforeCreate() {
    // klient i serwer
    console.log('[error beforeCreate]')
  },
  created() {
    // klient i serwer
    console.log('[error created, error=]', this.error)
  },
  beforeMount() {
    // tylko klient
    console.log('[error beforeMount]')
  },
  mounted() {
    // tylko klient
    console.log('[error mounted]')
  }
}
</script>

Gdy strona [nuxt] wyświetla stronę [error.vue], przekazuje jej jako właściwość [props] błąd, który wystąpił (wiersz 33). Jeśli błąd został wywołany przez [context.error(objet1)], właściwość [props] strony [error.vue] będzie miała wartość [objet1]. Dokumentacja [nuxt] wskazuje, że [objet1] musi posiadać co najmniej atrybuty [statusCode, message]. Wiersz 9 wyświetla ciąg znaków jSON z otrzymanego obiektu [objet1].

14.5. Strona [page2]

Strona [page2] pokazuje inny sposób obsługi błędu:

  • w [page1] błąd jest wyświetlany na osobnej stronie [error.vue];
  • w [page2] błąd zostanie wyświetlony na stronie [page2], która spowodowała błąd;

Kod dla [page2] jest następujący:


<!-- widok nr 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- nawigacja -->
    <Navigation slot="left" />
    <!-- komunikat -->
    <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',
  // używane komponenty
  components: {
    Layout,
    Navigation
  },
  // dane asynchroniczne
  asyncData(context) {
    // log
    console.log('[page2 asyncData started]')
    // początek oczekiwania
    context.app.$eventBus().$emit('loading', true)
    // brak błędu
    context.app.$eventBus().$emit('errorLoading', false)
    // zwracana jest obietnica
    return new Promise(function(resolve, reject) {
      // symulacja funkcji asynchronicznej
      setTimeout(function() {
        // koniec oczekiwania
        context.app.$eventBus().$emit('loading', false)
        // generuje się arbitralnie błąd
        const errorLoadingMessage = "le serveur n'a pas répondu assez vite"
        // pomyślne zakończenie
        resolve({ showErrorLoading: true, errorLoadingMessage })
        // log
        console.log('[page2 asyncData finished]')
      }, 5000)
    })
  },
  // cykl życia
  beforeCreate() {
    console.log('[page2 beforeCreate]')
  },
  created() {
    console.log('[page2 created]')
  },
  beforeMount() {
    console.log('[page2 beforeMount]')
  },
  mounted() {
    console.log('[page2 mounted]')
    // klient
    if (this.showErrorLoading) {
      console.log('[page2 mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
  }
}
</script>

Ponownie wstawiamy funkcję [asyncData] do kodu strony i podobnie jak w przypadku [index], [page2] wygeneruje błąd, który tym razem obsłużymy inaczej.

  • wiersz 44: zarówno serwer, jak i klient kończą obietnicę pomyślnie, zwracając wynik [{ showErrorLoading: true, errorLoadingMessage }]. Wiemy, że spowoduje to włączenie właściwości [showerrorLoading, errorLoadingMessage] do właściwości [data] strony oraz że klient otrzyma te właściwości;
  • wiersze 60–67: wiadomo, że funkcja [mounted] jest wykonywana wyłącznie przez klienta;
  • wiersz 63: klient sprawdza, czy właściwość [showErrorLoading] została ustawiona (przez serwer lub klienta, w zależności od sytuacji). Jeśli tak, wysyła zdarzenie [‘errorLoading’] (wiersz 65), aby strona [default] wyświetliła komunikat o błędzie [this.errorLoadingMessage]. Ostatecznie serwer wysyła stronę bez wyświetlonego komunikatu o błędzie. Komunikat ten jest wyświetlany w ostatniej chwili przez klienta podczas „wczytywania” strony;

14.6. Exécution

14.6.1. [nuxt.config]

Plik wykonawczy [nuxt.config.js] wygląda następująco:


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: [
    // Dokumentacja: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Dokumentacja: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Dokumentacja: 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) {}
  },
  // katalog kodu źródłowego
  srcDir: 'nuxt-11',
  // router
  router: {
    // katalog główny aplikacji URL
    base: '/nuxt-11/'
  },
  // serwer
  server: {
    // port usługi, domyślnie 3000
    port: 81,
    // adresy sieciowe, na których nasłuchuje, domyślnie localhost: 127.0.0.1
    // 0.0.0.0 = wszystkie adresy sieciowe komputera
    host: 'localhost'
  }
}
  • wiersz 22: ustawiamy właściwość [loading] na [false], aby [nuxt] nie korzystał ze swojego domyślnego obrazu oczekiwania;
  • wiersz 31: wtyczka definiująca magistralę zdarzeń;

14.6.2. L – strona [index] wygenerowana przez serwer

Wywołajmy stronę [index] na serwerze (wpisujemy ręcznie URL [http://localhost:81/nuxt-11/]). Strona wyświetlana przez przeglądarkę klienta wygląda następująco:

Image

Logi mają następującą postać:

Image

  • w [3] widać, że serwer wysyła stronę [error.vue];
  • w [4] widać, że klient również wyświetla stronę [error] z tym samym błędem co serwer;
  • można zauważyć, że metoda [mShowLoading] strony [default] nie została wywołana po stronie serwera, mimo że strona [index] uruchomiła oczekiwanie. Metoda ta jest wywoływana po otrzymaniu zdarzenia i najwyraźniej obsługa zdarzeń nie została zaimplementowana po stronie serwera;

Przyjrzyjmy się kodowi źródłowemu strony otrzymanej przez przeglądarkę klienta:


<!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>
  • wiersz 57: widać, że serwer wysłał obiekt [d], który reprezentuje błąd, który wystąpił po stronie serwera;
  • wiersz 59: widzimy właściwość [error], której wartością jest obiekt [d]. Można przypuszczać, że to właśnie obecność właściwości [error] na stronie wysłanej przez serwer powoduje, że skrypty po stronie klienta wyświetlają stronę [error.vue] wraz z błędem [error];

14.6.3. Strona [page1] wygenerowana przez serwer

Ręcznie wpisujemy URL [http://localhost:81/nuxt-11/page1]. Po 5 sekundach przeglądarka wyświetla następującą stronę:

Image

Wyświetlone logi są następujące:

Image

  • w [1] – logi serwera. Można zauważyć, że metoda [mShowLoading] ze strony [default] nie została wywołana;
  • w [2] – logi klienta;

14.6.4. Strona [page2] uruchomiona przez serwer

Ręcznie wpisujemy adres URL [http://localhost:81/nuxt-11/page2]. Po 5 sekundach przeglądarka wyświetla następującą stronę:

Image

Przyjrzyjmy się logom wyświetlanym w przeglądarce:

Image

  • w [1] – logi serwera. Przypomnijmy, że serwer umieścił właściwości [showErrorLoading, errorLoadingMessage] na stronie wysłanej do przeglądarki klienta. Wiemy, że właściwości te zostaną wówczas włączone do [data] strony wyświetlanej przez klienta
  • w [3], gdy strona [page2] jest ładowana, wykrywa, że właściwość [showErrorLoading] ma wartość true. Następnie wysyła zdarzenie do strony [default], aby wyświetliła komunikat o błędzie wysłany przez serwer [4];

14.6.5. Strona [index] uruchamiana przez klienta

Teraz używamy linków nawigacyjnych, aby wyświetlić te trzy strony. Wszystkie strony wyświetlane przez klienta są identyczne z tymi wyświetlanymi przez serwer. Jedyną różnicą jest to, że za każdym razem wyświetlany jest obrazek oczekiwania na upływ 5 sekund.

Zaczynamy od strony [index]. Wyświetla się wówczas obrazek oczekiwania:

Image

a po 5 sekundach pojawia się następująca strona:

Image

Ostateczna strona jest zatem identyczna z tą wygenerowaną po stronie serwera.

Image

Przypomnijmy funkcję [asyncData] ze strony [index]:


asyncData(context) {
    // log
    console.log('[page1 asyncData started]')
    // początek oczekiwania
    context.app.$eventBus().$emit('loading', true)
    // brak błędu
    context.app.$eventBus().$emit('errorLoading', false)
    // zwracamy obietnicę
    return new Promise(function(resolve, reject) {
      // symulacja funkcji asynchronicznej
      setTimeout(function() {
        // koniec oczekiwania
        context.app.$eventBus().$emit('loading', false)
        // log
        console.log('[page1 asyncData finished]')
        // zwracamy błąd
        reject(new Error("le serveur n'a pas répondu assez vite"))
      }, 5000)
    }).catch((e) => context.error({ statusCode: 500, message: e.message }))
}

Logi klienta są następujące:

Image

  • w [1] uruchamia się funkcja [asyncData];
  • w [2] uruchamiany jest obraz oczekiwania;
  • w [2-3] widać, że strona [default] otrzymała zdarzenia [loading, true], [2] i [errorLoading, false] wysłane przez funkcję [asyncData] ze strony [index] (wiersze 5 i 7);
  • w [4] zakończono oczekiwanie. Strona [default] otrzymała zdarzenie [loading, false] wysłane przez stronę [index] (wiersz 13);
  • w [5] funkcja [asyncData] zakończyła swoją pracę;
  • ponieważ funkcja [asyncData] spowodowała błąd w funkcji [context.error] (wiersz 19), wyświetlana jest strona [error] [6];

14.6.6. Strona [page1] wyświetlana przez klienta

Po upływie 5 sekund klient wyświetla następującą stronę:

Image

Przypomnijmy kod funkcji [asyncData] z [page1]:


asyncData(context) {
    // log
    console.log('[page1 asyncData started]')
    // początek oczekiwania
    context.app.$eventBus().$emit('loading', true)
    // brak błędu
    context.app.$eventBus().$emit('errorLoading', false)
    // zwracana jest obietnica
    return new Promise(function(resolve, reject) {
      // symuluje się funkcję asynchroniczną
      setTimeout(function() {
        // koniec oczekiwania
        context.app.$eventBus().$emit('loading', false)
        // log
        console.log('[page1 asyncData finished]')
        // zwracamy wynik asynchroniczny – w tym przypadku liczbę losową
        resolve({ result: Math.floor(Math.random() * Math.floor(100)) })
      }, 5000)
    })
},

Dzienniki mają następującą postać:

Image

14.6.7. Strona [page2] wyświetlana przez klienta

Po upływie 5 sekund klient wyświetla następującą stronę:

Image

Przypomnijmy kod funkcji [asyncData] i [mounted] z funkcji [page2]:


asyncData(context) {
    // log
    console.log('[page2 asyncData started]')
    // początek oczekiwania
    context.app.$eventBus().$emit('loading', true)
    // brak błędu
    context.app.$eventBus().$emit('errorLoading', false)
    // zwracamy obietnicę
    return new Promise(function(resolve, reject) {
      // symulujemy funkcję asynchroniczną
      setTimeout(function() {
        // koniec oczekiwania
        context.app.$eventBus().$emit('loading', false)
        // generuje się arbitralnie błąd
        const errorLoadingMessage = "le serveur n'a pas répondu assez vite"
        // pomyślne zakończenie
        resolve({ showErrorLoading: true, errorLoadingMessage })
        // log
        console.log('[page2 asyncData finished]')
      }, 5000)
    })
  }

mounted() {
    console.log('[page2 mounted]')
    // klient
    if (this.showErrorLoading) {
      console.log('[page2 mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
}

Dzienniki mają następującą postać:

Image

  • w [1] strona [default] otrzymała zdarzenie [showErrorLoading, true] wysłane przez [page2] (wiersz 29), które nakazuje jej wyświetlenie komunikatu o błędzie;