14. Exemplo [nuxt-11]: personalização da imagem de espera
Por padrão, a imagem de espera da função [nuxt] é uma barra de progresso. O exemplo [nuxt-11] mostra que é possível substituí-la por uma imagem de espera personalizada:

O exemplo [nuxt-11] também mostra como lidar com erros de carregamento.

O exemplo [nuxt-11] é obtido inicialmente por meio da cópia do exemplo [nuxt-10]:

Vamos adicionar, no [1], um plugin para o cliente cuja função será gerenciar eventos entre componentes.
14.1. O plug-in [event-bus]
O plug-in [event-bus] será executado pelo cliente e pelo servidor, mas veremos que ele não funciona no lado do servidor. Seu código é o seguinte:
// cria-se um barramento de eventos entre as visualizações
import Vue from 'vue'
export default (context, inject) => {
// o barramento de eventos
const eventBus = new Vue()
// injeção de uma função [eventBus] no contexto
inject('eventBus', () => eventBus)
}
- linha 5: o barramento de eventos é uma instância da classe [Vue]. De fato, essa classe possui métodos para gerenciar eventos:
- [$emit]: para emitir um evento;
- [$on]: para escutar um evento específico;
Esse barramento de eventos administrará apenas um evento, [loading], que será utilizado pelas páginas para iniciar/interromper a animação de espera pelo término de uma função assíncrona;
- linha 7: cria-se uma função [$eventBus] (1º argumento), cuja função será retornar o objeto [eventBus] que acabamos de criar (2º argumento). Essa função é injetada no contexto para que fique disponível nos objetos [context.app] e [this] das páginas;
14.2. O layout [default.vue]
O layout [default.vue] sofre as seguintes alterações:
<template>
<div class="container">
<b-card>
<!-- uma mensagem -->
<b-alert show variant="success" align="center">
<h4>[nuxt-11] : personnalisation de l'attente, gestion des erreurs</h4>
</b-alert>
<!-- a visualização atual do roteamento -->
<nuxt />
<!-- carregando -->
<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>
<!-- erro de carregamento -->
<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
}
},
// ciclo de vida
beforeCreate() {
console.log('[default beforeCreate]')
},
created() {
console.log('[default created]')
// escutando o evento [loading]
this.$eventBus().$on('loading', this.mShowLoading)
// assim como o evento [errorLoadingMessage]
this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
},
beforeMount() {
console.log('[default beforeMount]')
},
mounted() {
console.log('[default mounted]')
},
methods: {
// gerenciamento do carregamento
mShowLoading(value) {
console.log('[default mShowLoading], showLoading=', value)
this.showLoading = value
},
// erro de carregamento
mShowErrorLoading(value, errorLoadingMessage) {
console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
this.showErrorLoading = value
this.errorLoadingMessage = errorLoadingMessage
}
}
}
</script>
- linhas 11-14: a animação de espera. Ela só é exibida se a propriedade [showLoading] for verdadeira (linha 29);
- linhas 16-18: a mensagem de erro de carregamento. Ela só é exibida se a propriedade [showErrorLoading] (linha 30) for verdadeira;
- linhas 29-30: no carregamento inicial do componente, a animação de espera é ocultada, assim como a mensagem de erro;
- linhas 37-43: ao ser criada, a página escuta o evento [loading] (1º argumento) no barramento de eventos criado pelo plug-in. Ao recebê-lo, ela executa o método [mShowLoading] das linhas 52-55 (2º argumento);
- linhas 52-55: o valor recebido pelo método [mShowLoading] será um booleano (true/false). Ele serve para mostrar ou ocultar a mensagem de espera;
- linhas 41-42: ao ser criada, a página escuta o evento [errorLoading] (1º argumento) no barramento de eventos criado pelo plug-in. Ao recebê-lo, ela executa o método [mShowErrorLoading] das linhas 57-61 (2º argumento);
- linha 57: o método [mShowErrorLoading] recebe dois argumentos:
- o primeiro argumento é um valor booleano (true/false) para mostrar ou ocultar a mensagem de erro;
- o segundo argumento só está presente se tiver ocorrido um erro. Ele representa a mensagem de erro a ser exibida;
- os registros das linhas 53 e 58 nos mostrarão que os métodos [showLoading] e [showErrorLoading] não são executados no lado do servidor;
14.3. A página [page1]
O código da página [page1] evolui da seguinte forma:
<!-- vista n.º 1 -->
<template>
<Layout :left="true" :right="true">
<!-- navegação -->
<Navigation slot="left" />
<!-- mensagem-->
<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',
// componentes utilizados
components: {
Layout,
Navigation
},
// dados assíncronos
asyncData(context) {
// log
console.log('[page1 asyncData started]')
// início da espera
context.app.$eventBus().$emit('loading', true)
// sem erro
context.app.$eventBus().$emit('errorLoading', false)
// retornando uma promessa
return new Promise(function(resolve, reject) {
// simulando uma função assíncrona
setTimeout(function() {
// fim da espera
context.app.$eventBus().$emit('loading', false)
// registro
console.log('[page1 asyncData finished]')
// retornando o resultado assíncrono — um número aleatório aqui
resolve({ result: Math.floor(Math.random() * Math.floor(100)) })
}, 5000)
})
},
// ciclo de vida
beforeCreate() {
console.log('[page1 beforeCreate]')
},
created() {
console.log('[page1 created]')
},
beforeMount() {
console.log('[page1 beforeMount]')
},
mounted() {
console.log('[page1 mounted]')
}
}
</script>
- as modificações ocorrem na função [asyncData], nas linhas 26 a 47;
- linhas 29-30: antes do início da função assíncrona, é emitido o evento [loading] para as demais páginas do aplicativo. Vale lembrar que, na função [asyncData], não há acesso ao objeto [this], que ainda não foi criado. Portanto, utiliza-se o contexto recebido como argumento pela função [asyncData] (linha 26);
- linha 30: utiliza-se o barramento de eventos para indicar que o carregamento está prestes a começar;
- linha 38: utiliza-se o barramento de eventos para indicar que o carregamento foi concluído;
Observação: Durante a execução, quando a página [page1] é solicitada ao servidor, a imagem de espera não é exibida. Nos logs, verifica-se que, no lado do servidor, o método [default.mShowLoading] não é chamado. De qualquer forma, ver a imagem de espera não faz sentido quando a página é solicitada ao servidor. Este só envia a página ao navegador do cliente após a conclusão da função [asyncData]. A imagem de espera torna-se, então, desnecessária. Esse será o caso para todas as páginas do aplicativo solicitadas diretamente ao servidor.
14.4. A página [index]
O código da página [index] é o seguinte:
<!-- página principal -->
<template>
<Layout :left="true" :right="true">
<!-- navegação -->
<Navigation slot="left" />
<!-- mensagem-->
<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',
// componentes utilizados
components: {
Layout,
Navigation
},
// dados assíncronos
asyncData(context) {
// log
console.log('[page1 asyncData started]')
// início da espera
context.app.$eventBus().$emit('loading', true)
// sem erro
context.app.$eventBus().$emit('errorLoading', false)
// retornando uma promessa
return new Promise(function(resolve, reject) {
// simulando uma função assíncrona
setTimeout(function() {
// fim da espera
context.app.$eventBus().$emit('loading', false)
// registro
console.log('[page1 asyncData finished]')
// retornando um erro
reject(new Error("le serveur n'a pas répondu assez vite"))
}, 5000)
}).catch((e) => context.error({ statusCode: 500, message: e.message }))
},
// ciclo de vida
beforeCreate() {
console.log('[home beforeCreate]')
},
created() {
console.log('[home created]')
},
beforeMount() {
console.log('[home beforeMount]')
},
mounted() {
console.log('[home mounted]')
// sem erro
this.$eventBus().$emit('errorLoading', false)
}
}
</script>
- linhas 30-49: a função [asyncData] é idêntica à da página [page1], com uma única diferença: na linha 46, a função assíncrona é encerrada em caso de falha (uso do método [reject]);
- linha 46: o parâmetro da função [reject] é uma instância da classe [Error]. O parâmetro do construtor [Error] é a mensagem de erro;
- linha 48: esse erro é interceptado pelo método [catch] da classe [Promise], que recebe o erro como parâmetro. Em seguida, utiliza-se a função [context.error] para declarar o erro. O parâmetro da função [context.error] é um objeto com duas propriedades:
- [statusCode]: um código de erro HTTP;
- [message]: uma mensagem de erro;
Independentemente de a função [asyncData] ser executada pelo cliente ou pelo servidor, em caso de erro [context.error], a função [nuxt] exibe a página [layouts / error.vue]:

Embora seja uma página, a página [error.vue] é procurada na pasta [layouts] (talvez para evitar que seja incluída nas rotas do aplicativo?). Aqui, a página [error.vue] é a seguinte:
<!-- definição HTML da visualização -->
<template>
<!-- formatação -->
<Layout :left="true" :right="true">
<!-- alerta na coluna da direita -->
<template slot="right">
<!-- mensagem em fundo amarelo -->
<b-alert show variant="danger" align="center">
<h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
</b-alert>
</template>
<!-- menu de navegação na coluna da esquerda -->
<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',
// componentes utilizados
components: {
Layout,
Navigation
},
// propriedade [props]
props: { error: { type: Object, default: () => 'waiting ...' } },
// ciclo de vida
beforeCreate() {
// cliente e servidor
console.log('[error beforeCreate]')
},
created() {
// cliente e servidor
console.log('[error created, error=]', this.error)
},
beforeMount() {
// somente cliente
console.log('[error beforeMount]')
},
mounted() {
// somente cliente
console.log('[error mounted]')
}
}
</script>
Quando [nuxt] exibe a página [error.vue], ela passa para ela, como propriedade, [props], o erro que ocorreu (linha 33). Se o erro tiver sido causado por [context.error(objet1)], a propriedade [props] da página [error.vue] terá o valor [objet1]. A documentação [nuxt] indica que [objet1] deve ter, no mínimo, os atributos [statusCode, message]. A linha 9 exibe a cadeia jSON do objeto [objet1] recebido.
14.5. A página [page2]
A página [page2] mostra outra maneira de lidar com o erro:
- em [page1], o erro é exibido em uma página separada, [error.vue];
- em [page2], o erro será exibido na página [page2], que causou o erro;
O código de [page2] é o seguinte:
<!-- vista n.º 2 -->
<template>
<Layout :left="true" :right="true">
<!-- navegação -->
<Navigation slot="left" />
<!-- mensagem -->
<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',
// componentes utilizados
components: {
Layout,
Navigation
},
// dados assíncronos
asyncData(context) {
// log
console.log('[page2 asyncData started]')
// início da espera
context.app.$eventBus().$emit('loading', true)
// sem erro
context.app.$eventBus().$emit('errorLoading', false)
// retornando uma promessa
return new Promise(function(resolve, reject) {
// simulando uma função assíncrona
setTimeout(function() {
// fim da espera
context.app.$eventBus().$emit('loading', false)
// gerando um erro arbitrário
const errorLoadingMessage = "le serveur n'a pas répondu assez vite"
// concluído com sucesso
resolve({ showErrorLoading: true, errorLoadingMessage })
// registro no log
console.log('[page2 asyncData finished]')
}, 5000)
})
},
// ciclo de vida
beforeCreate() {
console.log('[page2 beforeCreate]')
},
created() {
console.log('[page2 created]')
},
beforeMount() {
console.log('[page2 beforeMount]')
},
mounted() {
console.log('[page2 mounted]')
// cliente
if (this.showErrorLoading) {
console.log('[page2 mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
Mais uma vez, insere-se uma função [asyncData] no código da página e, assim como [index], [page2] irá gerar um erro que, desta vez, será tratado de maneira diferente.
- linha 44: tanto o servidor quanto o cliente concluem a promessa com sucesso, retornando o resultado [{ showErrorLoading: true, errorLoadingMessage }]. Sabemos que isso terá como efeito incluir as propriedades [showerrorLoading, errorLoadingMessage] nas propriedades [data] da página e que o cliente receberá essas propriedades;
- linhas 60-67: sabe-se que a função [mounted] é executada apenas pelo cliente;
- linha 63: o cliente verifica se a propriedade [showErrorLoading] foi definida (pelo servidor ou pelo cliente, conforme o caso). Se sim, ele emite o evento [‘errorLoading’] (linha 65) para que a página [default] exiba a mensagem de erro [this.errorLoadingMessage]. No final, o servidor envia uma página sem nenhuma mensagem de erro exibida. Essa mensagem é exibida no último momento pelo cliente quando a página é “carregada”;
14.6. Exécution
14.6.1. [nuxt.config]
O arquivo de execução [nuxt.config.js] é o seguinte:
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',
// Documentação: 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) {}
},
// diretório do código-fonte
srcDir: 'nuxt-11',
// roteador
router: {
// raiz da aplicação URL
base: '/nuxt-11/'
},
// servidor
server: {
// porta de serviço, 3000 por padrão
port: 81,
// endereços de rede monitorados, por padrão localhost: 127.0.0.1
// 0.0.0.0 = todos os endereços de rede da máquina
host: 'localhost'
}
}
- linha 22: define-se a propriedade [loading] como [false] para que [nuxt] não utilize sua imagem de espera padrão;
- linha 31: o plugin que define o barramento de eventos;
14.6.2. L, uma página [index] executada pelo servidor
Vamos solicitar a página [index] ao servidor (digitamos URL [http://localhost:81/nuxt-11/] manualmente). A página exibida pelo navegador do cliente é a seguinte:

Os registros são os seguintes:

- em [3], vemos que o servidor envia a página [error.vue];
- em [4], percebe-se que o cliente também exibe a página [error] com o mesmo erro do servidor;
- pode-se observar que o método [mShowLoading] da página [default] não foi chamado no lado do servidor, embora a página [index] tivesse ativado uma espera. Esse método é chamado ao receber um evento e, aparentemente, o gerenciamento de eventos não está implementado no lado do servidor;
Vamos examinar o código-fonte da página recebida pelo navegador do cliente:
<!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 : {"statusCode":500,"message":"le serveur n'a pas répondu assez vite"}</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>
- linha 57: vemos que o servidor enviou um objeto [d], que representa o erro ocorrido no lado do servidor;
- linha 59: vemos uma propriedade [error] cujo valor é o objeto [d]. É possível imaginar que seja a presença da propriedade [error] na página enviada pelo servidor que faz com que os scripts do cliente exibam a página [error.vue] com o erro [error];
14.6.3. A página [page1] executada pelo servidor
Digita-se manualmente URL [http://localhost:81/nuxt-11/page1]. Após 5 segundos, o navegador exibe a seguinte página:

Os logs exibidos são os seguintes:

- em [1], os logs do servidor. É possível observar que o método [mShowLoading] da página [default] não foi chamado;
- em [2], os logs do cliente;
14.6.4. A página [page2] executada pelo servidor
Digitamos manualmente URL [http://localhost:81/nuxt-11/page2]. Após 5 segundos, o navegador exibe a seguinte página:

Vamos examinar os logs exibidos no navegador:

- em [1], os logs do servidor. Vale lembrar que o servidor inseriu as propriedades [showErrorLoading, errorLoadingMessage] na página enviada ao navegador do cliente. Sabemos que, então, essas propriedades serão integradas nos [data] da página exibida pelo cliente
- em [3]; quando a página [page2] é carregada, ela encontra a propriedade [showErrorLoading] com valor verdadeiro. Em seguida, ela envia um evento para a página [default], para que esta exiba a mensagem de erro enviada pelo servidor [4];
14.6.5. A página [index] executada pelo cliente
Agora, usamos os links de navegação para exibir as três páginas. Todas as páginas exibidas pelo cliente são idênticas às exibidas pelo servidor. A única diferença é que a imagem de espera ao final dos 5 segundos é exibida todas as vezes.
Começamos pela página [index]. A imagem de espera é então exibida:

e, após 5 segundos, obtém-se a página seguinte:

A página final é, portanto, idêntica à obtida no lado do servidor.

Vale lembrar a função [asyncData] da página [index]:
asyncData(context) {
// log
console.log('[page1 asyncData started]')
// início da espera
context.app.$eventBus().$emit('loading', true)
// sem erros
context.app.$eventBus().$emit('errorLoading', false)
// retornando uma promessa
return new Promise(function(resolve, reject) {
// simulando uma função assíncrona
setTimeout(function() {
// fim da espera
context.app.$eventBus().$emit('loading', false)
// registro
console.log('[page1 asyncData finished]')
// retornando um erro
reject(new Error("le serveur n'a pas répondu assez vite"))
}, 5000)
}).catch((e) => context.error({ statusCode: 500, message: e.message }))
}
Os logs do cliente são os seguintes:

- na [1], a função [asyncData] é iniciada;
- em [2], a imagem de espera é ativada;
- em [2-3], percebe-se que a página [default] recebeu os eventos [loading, true], [2] e [errorLoading, false] enviados pela função [asyncData] da página [index] (linhas 5 e 7);
- em [4], fim da espera. A página [default] recebeu o evento [loading, false] enviado pela página [index] (linha 13);
- em [5], a função [asyncData] concluiu seu trabalho;
- como a função [asyncData] gerou um erro com [context.error] (linha 19), a página [error] é exibida como [6];
14.6.6. A página [page1] executada pelo cliente
Após uma espera de 5 segundos, o cliente exibe a seguinte página:

Recordemos o código da função [asyncData] a partir de [page1]:
asyncData(context) {
// registro
console.log('[page1 asyncData started]')
// início da espera
context.app.$eventBus().$emit('loading', true)
// sem erro
context.app.$eventBus().$emit('errorLoading', false)
// retorna uma promessa
return new Promise(function(resolve, reject) {
// simulando uma função assíncrona
setTimeout(function() {
// fim da espera
context.app.$eventBus().$emit('loading', false)
// registro
console.log('[page1 asyncData finished]')
// retornando o resultado assíncrono — um número aleatório aqui
resolve({ result: Math.floor(Math.random() * Math.floor(100)) })
}, 5000)
})
},
Os logs são os seguintes:

14.6.7. A página [page2] executada pelo cliente
Após uma espera de 5 segundos, o cliente exibe a seguinte página:

Recordemos o código das funções [asyncData] e [mounted] de [page2]:
asyncData(context) {
// log
console.log('[page2 asyncData started]')
// início da espera
context.app.$eventBus().$emit('loading', true)
// sem erro
context.app.$eventBus().$emit('errorLoading', false)
// retornando uma promessa
return new Promise(function(resolve, reject) {
// simulando uma função assíncrona
setTimeout(function() {
// fim da espera
context.app.$eventBus().$emit('loading', false)
// gerando um erro arbitrário
const errorLoadingMessage = "le serveur n'a pas répondu assez vite"
// concluído com sucesso
resolve({ showErrorLoading: true, errorLoadingMessage })
// registro
console.log('[page2 asyncData finished]')
}, 5000)
})
}
mounted() {
console.log('[page2 mounted]')
// cliente
if (this.showErrorLoading) {
console.log('[page2 mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
Os logs são os seguintes:

- no [1], a página [default] recebeu o evento [showErrorLoading, true] enviado por [page2] (linha 29), que solicita que ela exiba a mensagem de erro;