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] من قبل كل من العميل والخادم، لكننا سنرى أنه لا يعمل على جانب الخادم. وفيما يلي كوده:


// on crée un bus d'événements entre les vues
import Vue from 'vue'
export default (context, inject) => {
  // le bus d'événements
  const eventBus = new Vue()
  // injection d'une fonction [eventBus] dans le contexte
  inject('eventBus', () => eventBus)
}
  • السطر 5: ناقل الأحداث هو مثيل لفئة [Vue]. توفر هذه الفئة طرقًا للتعامل مع الأحداث:
    • [$emit]: لإصدار حدث؛
    • [$on]: للاستماع إلى حدث معين؛

ستتعامل حافلة الأحداث هذه مع حدث واحد فقط، وهو [loading]، والذي ستستخدمه الصفحات لبدء/إيقاف الرسوم المتحركة للتحميل أثناء انتظار اكتمال وظيفة غير متزامنة؛

  • السطر 7: نقوم بإنشاء دالة [$eventBus] (الحجة الأولى) التي ستكون مهمتها إرجاع كائن [eventBus] الذي أنشأناه للتو (الحجة الثانية). يتم حقن هذه الدالة في السياق بحيث تكون متاحة في كائني [context.app] و [this] للصفحات؛

14.2. تخطيط [default.vue]

يتطور تخطيط [default.vue] على النحو التالي:


<template>
  <div class="container">
    <b-card>
      <!-- un message -->
      <b-alert show variant="success" align="center">
        <h4>[nuxt-11] : personnalisation de l'attente, gestion des erreurs</h4>
      </b-alert>
      <!-- la vue courante du routage -->
      <nuxt />
      <!-- loading -->
      <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>
      <!-- erreur de chargement -->
      <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
    }
  },
  // life cycle
  beforeCreate() {
    console.log('[default beforeCreate]')
  },
  created() {
    console.log('[default created]')
    // listen to the evt [loading]
    this.$eventBus().$on('loading', this.mShowLoading)
    // and the [errorLoadingMessage] event
    this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
  },
  beforeMount() {
    console.log('[default beforeMount]')
  },
  mounted() {
    console.log('[default mounted]')
  },
  methods: {
    // load management
    mShowLoading(value) {
      console.log('[default mShowLoading], showLoading=', value)
      this.showLoading = value
    },
    // loading error
    mShowErrorLoading(value, errorLoadingMessage) {
      console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
      this.showErrorLoading = value
      this.errorLoadingMessage = errorLoadingMessage
    }
  }
}
</script>
  • الأسطر 11–14: الرسوم المتحركة للتحميل. يتم عرضها فقط إذا كانت الخاصية [showLoading] صحيحة (السطر 29)؛
  • الأسطر 16–18: رسالة خطأ التحميل. يتم عرضها فقط إذا كانت الخاصية [showErrorLoading] (السطر 30) صحيحة؛
  • السطور 29-30: عند تحميل المكون لأول مرة، يتم إخفاء الرسوم المتحركة للتحميل، وكذلك رسالة الخطأ؛
  • الأسطر 37–43: عند إنشائها، تستمع الصفحة إلى حدث [loading] (الحجة الأولى) على ناقل الأحداث الذي أنشأه المكون الإضافي. عند استلامه، تنفذ الطريقة [mShowLoading] في الأسطر 52–55 (الحجة الثانية)؛
  • الأسطر 52-55: ستكون القيمة التي تتلقاها طريقة [mShowLoading] من نوع Boolean (صحيح/خطأ). تُستخدم لإظهار أو إخفاء رسالة التحميل؛
  • الأسطر 41-42: عند إنشاء الصفحة، تستمع إلى حدث [errorLoading] (الحجة الأولى) على ناقل الأحداث الذي أنشأه المكون الإضافي. عند استلامه، تقوم بتنفيذ طريقة [mShowErrorLoading] في الأسطر 57-61 (الحجة الثانية)؛
  • السطر 57: تأخذ طريقة [mShowErrorLoading] حجتين:
    • الحجة الأولى هي قيمة منطقية (صحيح/خطأ) لإظهار أو إخفاء رسالة الخطأ؛
    • الحجة الثانية موجودة فقط في حالة حدوث خطأ. وهي تمثل رسالة الخطأ المراد عرضها؛
  • ستُظهر لنا السجلات في الأسطر 53 و58 أن طريقتي [showLoading] و[showErrorLoading] لا يتم تنفيذهما على جانب الخادم؛

14.3. الصفحة [page1]

يتغير كود الصفحة [page1] على النحو التالي:


<!-- vue n° 1 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigation -->
    <Navigation slot="left" />
    <!-- message-->
    <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 used
  components: {
    Layout,
    Navigation
  },
  // asynchronous data
  asyncData(context) {
    // log
    console.log('[page1 asyncData started]')
    // start waiting
    context.app.$eventBus().$emit('loading', true)
    // no error
    context.app.$eventBus().$emit('errorLoading', false)
    // we make a promise
    return new Promise(function(resolve, reject) {
      // we simulate an asynchronous function
      setTimeout(function() {
        // end waiting
        context.app.$eventBus().$emit('loading', false)
        // log
        console.log('[page1 asyncData finished]')
        // we make the result asynchronous - a random number here
        resolve({ result: Math.floor(Math.random() * Math.floor(100)) })
      }, 5000)
    })
  },
 
  // life cycle
  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]:


<!-- page principale -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigation -->
    <Navigation slot="left" />
    <!-- message-->
    <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 used
  components: {
    Layout,
    Navigation
  },
  // asynchronous data
  asyncData(context) {
    // log
    console.log('[page1 asyncData started]')
    // start waiting
    context.app.$eventBus().$emit('loading', true)
    // no error
    context.app.$eventBus().$emit('errorLoading', false)
    // we make a promise
    return new Promise(function(resolve, reject) {
      // we simulate an asynchronous function
      setTimeout(function() {
        // end waiting
        context.app.$eventBus().$emit('loading', false)
        // log
        console.log('[page1 asyncData finished]')
        // we return an error
        reject(new Error("le serveur n'a pas répondu assez vite"))
      }, 5000)
    }).catch((e) => context.error({ statusCode: 500, message: e.message }))
  },
  // life cycle
  beforeCreate() {
    console.log('[home beforeCreate]')
  },
  created() {
    console.log('[home created]')
  },
  beforeMount() {
    console.log('[home beforeMount]')
  },
  mounted() {
    console.log('[home mounted]')
    // no error
    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] كما يلي:


<!-- définition HTML de la vue -->
<template>
  <!-- mise en page -->
  <Layout :left="true" :right="true">
    <!-- alerte dans la colonne de droite -->
    <template slot="right">
      <!-- message sur fond jaune -->
      <b-alert show variant="danger" align="center">
        <h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
      </b-alert>
    </template>
    <!-- menu de navigation dans la colonne de gauche -->
    <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 used
  components: {
    Layout,
    Navigation
  },
  // property [props]
  props: { error: { type: Object, default: () => 'waiting ...' } },
  // life cycle
  beforeCreate() {
    // client and server
    console.log('[error beforeCreate]')
  },
  created() {
    // client and server
    console.log('[error created, error=]', this.error)
  },
  beforeMount() {
    // customer only
    console.log('[error beforeMount]')
  },
  mounted() {
    // customer only
    console.log('[error mounted]')
  }
}
</script>

عندما يقوم [nuxt] بعرض صفحة [error.vue]، فإنه يمرر الخطأ الذي حدث إليه كخاصية [props] (السطر 33). إذا كان الخطأ ناتجًا عن [context.error(object1)]، فستكون قيمة خاصية [props] لصفحة [error.vue] هي [object1]. تنص وثائق [nuxt] على أن [object1] يجب أن تحتوي على الأقل على سمات [statusCode, message]. يعرض السطر 9 سلسلة JSON للكائن [object1] المستلم.

14.5. الصفحة [page2]

تُظهر صفحة [page2] طريقة أخرى للتعامل مع الخطأ:

  • في [page1]، يتم عرض الخطأ في صفحة منفصلة [error.vue
  • في [page2]، سيتم عرض الخطأ على الصفحة [page2] التي تسببت في الخطأ؛

في [page1]، يتم عرض الخطأ في صفحة منفصلة [error.vue


<!-- vue n° 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigation -->
    <Navigation slot="left" />
    <!-- message -->
    <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 used
  components: {
    Layout,
    Navigation
  },
  // asynchronous data
  asyncData(context) {
    // log
    console.log('[page2 asyncData started]')
    // start waiting
    context.app.$eventBus().$emit('loading', true)
    // no error
    context.app.$eventBus().$emit('errorLoading', false)
    // we make a promise
    return new Promise(function(resolve, reject) {
      // we simulate an asynchronous function
      setTimeout(function() {
        // end waiting
        context.app.$eventBus().$emit('loading', false)
        // arbitrarily generate an error
        const errorLoadingMessage = "le serveur n'a pas répondu assez vite"
        // successful completion
        resolve({ showErrorLoading: true, errorLoadingMessage })
        // log
        console.log('[page2 asyncData finished]')
      }, 5000)
    })
  },
  // life cycle
  beforeCreate() {
    console.log('[page2 beforeCreate]')
  },
  created() {
    console.log('[page2 created]')
  },
  beforeMount() {
    console.log('[page2 beforeMount]')
  },
  mounted() {
    console.log('[page2 mounted]')
    // customer
    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. التنفيذ

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: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Doc: 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) {}
  },
  // source code directory
  srcDir: 'nuxt-11',
  // router
  router: {
    // application URL root
    base: '/nuxt-11/'
  },
  // server
  server: {
    // service port, default 3000
    port: 81,
    // network addresses listened to, default localhost: 127.0.0.1
    // 0.0.0.0 = all the machine's network addresses
    host: 'localhost'
  }
}
  • السطر 22: تعيين الخاصية [loading] إلى [false] حتى لا يستخدم [nuxt] صورة التحميل الافتراضية الخاصة به؛
  • السطر 31: المكون الإضافي الذي يحدد ناقل الأحداث؛

14.6.2. الصفحة [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) {
    // log
    console.log('[page1 asyncData started]')
    // start waiting
    context.app.$eventBus().$emit('loading', true)
    // no error
    context.app.$eventBus().$emit('errorLoading', false)
    // we make a promise
    return new Promise(function(resolve, reject) {
      // we simulate an asynchronous function
      setTimeout(function() {
        // end waiting
        context.app.$eventBus().$emit('loading', false)
        // log
        console.log('[page1 asyncData finished]')
        // we return an error
        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) {
    // log
    console.log('[page1 asyncData started]')
    // start waiting
    context.app.$eventBus().$emit('loading', true)
    // no error
    context.app.$eventBus().$emit('errorLoading', false)
    // we make a promise
    return new Promise(function(resolve, reject) {
      // we simulate an asynchronous function
      setTimeout(function() {
        // end waiting
        context.app.$eventBus().$emit('loading', false)
        // log
        console.log('[page1 asyncData finished]')
        // we make the result asynchronous - a random number here
        resolve({ result: Math.floor(Math.random() * Math.floor(100)) })
      }, 5000)
    })
},

السجلات هي كما يلي:

Image

14.6.7. الصفحة [page2] التي نفذها العميل

بعد انتظار 5 ثوانٍ، يعرض العميل الصفحة التالية:

Image

دعونا نراجع كود دالتي [asyncData] و [mounted] في [page2]:


asyncData(context) {
    // log
    console.log('[page2 asyncData started]')
    // start waiting
    context.app.$eventBus().$emit('loading', true)
    // no error
    context.app.$eventBus().$emit('errorLoading', false)
    // we make a promise
    return new Promise(function(resolve, reject) {
      // we simulate an asynchronous function
      setTimeout(function() {
        // end waiting
        context.app.$eventBus().$emit('loading', false)
        // arbitrarily generate an error
        const errorLoadingMessage = "le serveur n'a pas répondu assez vite"
        // successful completion
        resolve({ showErrorLoading: true, errorLoadingMessage })
        // log
        console.log('[page2 asyncData finished]')
      }, 5000)
    })
  }
 
mounted() {
    console.log('[page2 mounted]')
    // customer
    if (this.showErrorLoading) {
      console.log('[page2 mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
}

السجلات هي كما يلي:

Image

  • في [1]، تلقت الصفحة [default] الحدث [showErrorLoading, true] المرسل من [page2] (السطر 29)، والذي يوجهها لعرض رسالة الخطأ؛