Skip to content

5. Esempio [nuxt-02]: pagine server e client

In questo progetto mostriamo:

  • che la pagina costruita dal client può essere visivamente diversa da quella ricevuta dal server. Si ha quindi un rapido cambio di pagina, percepibile dall’utente, che risulta quindi dannoso per l’ergonomia dell’applicazione. Si tratta quindi di un’opzione da evitare;
  • una soluzione affinché la pagina client ricrei la stessa pagina inviata dal server;

Il progetto [nuxt-02] è stato ottenuto inizialmente copiando il progetto [nuxt-01].

Image

Al progetto viene aggiunta una cartella [store] insieme a due nuove pagine. Ci torneremo più avanti.

5.1. La pagina [index]

5.1.1. Il codice della pagina

Il codice della pagina [index] diventa il seguente:


<!-- pagina principale -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio-->
    <b-alert slot="right" show variant="warning"> Home - value= {{ value }} </b-alert>
  </Layout>
</template>

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

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

export default {
  name: 'Home',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[home beforeCreate]')
  },
  created() {
    // client e server
    console.log('[home created]')
    // solo server
    if (process.server) {
      this.value = 10
    }
    // client e server
    console.log('value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[home beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[home mounted]')
  }
}
</script>

Commenti

  • riga 7: la pagina [index] visualizzerà il valore della sua proprietà [value] (riga 28);
  • righe 36-45: occorre ricordare che la funzione [created] viene eseguita sia sul lato server che sul lato client. Alle righe 40-42, il server imposterà a 10 il valore della proprietà [value]. Il client, invece, non modifica questo valore. Vogliamo semplicemente sapere se questo valore viene conservato dal client. Scopriremo che non è così;

5.1.2. Esecuzione

Modifichiamo il file [/nuxt.config.js] per eseguire il progetto [nuxt-02]:


...
  // directory del codice sorgente
  srcDir: 'nuxt-02',
  // router
  router: {
    // radice dei file URL dell'applicazione
    base: '/nuxt-02/'
  },
  // server
  server: {
    // porta di servizio, 3000 per impostazione predefinita
    port: 81,
    // indirizzi di rete in ascolto, per impostazione predefinita localhost: 127.0.0.1
    // 0.0.0.0 = tutti gli indirizzi di rete del computer
    host: 'localhost'
  }
...

Eseguiamo il progetto [1]:

Image

Viene quindi visualizzata la pagina [index] [2-3]. Essa mostra il valore [10] per alcuni istanti, quindi visualizza il valore [0]. Cosa è successo?

Fase 1

Il server viene eseguito per primo. Esegue il codice della pagina [index]:


export default {
  name: 'Home',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[home beforeCreate]')
  },
  created() {
    // client e server
    console.log('[home created]')
    // solo server
    if (process.server) {
      this.value = 10
    }
    // client e server
    console.log('value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[home beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[home mounted]')
  }
}
  • a causa della riga 23, la proprietà [value] della riga 10 assume il valore 10;

È possibile verificarlo osservando il codice sorgente della pagina ricevuta dal browser (opzione [code source] nel browser):


<!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-02/">
  <link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_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-02] : page serveur, page client</h4>
            </div> <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-02/" target="_self" class="nav-link active nuxt-link-active">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page1" target="_self" class="nav-link">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/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" class="alert alert-warning">
                    Home - value= 10
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>window.__NUXT__ = ....;</script>
  <script src="/nuxt-02/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
  • riga 46: nella pagina ricevuta, [value] aveva il valore 10;

fase 2

Sappiamo che, dopo la ricezione della pagina, gli script delle righe 57-60 prendono il controllo e modificano il comportamento della pagina ricevuta, o addirittura le informazioni visualizzate, come in questo caso. Questi script costituiscono il client che a sua volta esegue il codice della pagina [index], lo stesso codice del server:


export default {
  name: 'Home',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[home beforeCreate]')
  },
  created() {
    // client e server
    console.log('[home created]')
    // solo server
    if (process.server) {
      this.value = 10
    }
    // client e server
    console.log('value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[home beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[home mounted]')
  }
}
  • per comprendere cosa sta accadendo, occorre tenere presente che il client [nuxt] non eseguirà le righe 22-24 (process.server=false);
  • in un’applicazione [vue] classica, la proprietà [value] della riga 10 rimane a 0. Ecco perché, una volta che il client è passato alla pagina ricevuta, il valore visualizzato diventa [0];

Il valore generato dal server [nuxt] per la proprietà [value] non è servito a nulla.

5.2. La pagina [page1]

5.2.1. Lo store [Vuex]

Abbiamo aggiunto una cartella [store] al progetto [nuxt-02]:

Image

La presenza di questa cartella fa sì che [nuxt] implementi automaticamente uno store [Vuex]. È il file [index.js] che implementa questo archivio. In questo caso, il file [index.js] è il seguente:


export const state = () => ({
  counter: 0
})

export const mutations = {
  increment(state, inc) {
    state.counter += inc
  }
}

[nuxt] implementa uno store [Vuex] a partire dal contenuto di [index.js]:

  • righe 1-3: definizione dello stato [state] dello store. Questo stato viene restituito da una funzione. In questo caso, lo stato ha una sola proprietà, il contatore della riga 2. La funzione esportata deve chiamarsi [state];
  • righe 5-9: le operazioni possibili sullo stato dello store. Si chiamano [mutations]. Qui, la mutazione [increment] consente di incrementare la proprietà [counter] di un valore [inc]. L’oggetto esportato deve chiamarsi [mutations];

Il [store] Vuex implementato da [nuxt] è disponibile in diversi punti. Nelle viste, è disponibile nella proprietà [this.$store].

5.2.2. Il codice della pagina

Come la pagina [index], anche la pagina [page1] visualizzerà un valore, ovvero quello del contatore dello store Vuex:


<!-- pagina 1 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio-->
    <b-alert slot="right" show variant="primary"> Page 1 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */

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

export default {
  name: 'Page1',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[home beforeCreate]')
  },
  created() {
    // client e server
    console.log('[home created]')
    // solo server
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // client e server
    this.value = this.$store.state.counter
    console.log('value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[home beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[home mounted]')
  }
}
</script>

Commenti

  • righe 38-40: il server incrementerà il contatore di 25;
  • riga 42: sia il server che il client visualizzeranno il valore del contatore;
  • riga 7: viene visualizzato il valore del contatore;

Leggendo questo codice, occorre comprendere due cose:

  • il codice eseguito è lo stesso sia per il server che per il client;
  • l’oggetto [this] non è invece lo stesso: esiste una versione [this] sul lato server e un’altra sul lato client, [client];

Stiamo cercando di capire se il [this.$store] del server sia lo stesso del [this.$store] del client. Poiché è il server a essere eseguito per primo (all’avvio dell’applicazione), ciò equivale a chiedersi: il [store] inizializzato dal server viene trasmesso al client?

5.2.3. Esecuzione

Si esegue il progetto [nuxt-02] e si digita manualmente [localhost:81/nuxt-02/page1] per far sì che il server venga interpellato. Come all’avvio per la pagina [index]:

  • il server esegue la pagina [page1.vue];
  • invia la pagina generata al browser. Questa viene visualizzata;
  • gli script client incorporati nella pagina inviata prendono il controllo ed eseguono nuovamente la pagina [page1.vue];
  • la pagina visualizzata viene quindi modificata;

Il risultato finale è il seguente:

Image

Questa volta, il valore visualizzato è effettivamente quello impostato dal server e, visivamente, non si nota alcun «sbalzo» della pagina dovuto a una modifica da parte del client del valore visualizzato dal server. Cosa è successo questa volta?

Il server ha eseguito la pagina [page1] seguente:


...

<script>
/* eslint-disable no-console */

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

export default {
  name: 'Page1',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[page1 beforeCreate]')
  },
  created() {
    // client e server
    console.log('[page1 created]')
    // solo server
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // client e server
    this.value = this.$store.state.counter
    console.log('value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[page1 beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[page1 mounted]')
  }
}
</script>
  • le righe 30-32 sono state eseguite senza errori. Ciò significa che anche lato server, [this.$store] indica lo store [Vuex]. La riga 31 ha portato il contatore dello store a 25;
  • fatto ciò, la pagina è stata inviata al client;

Se si osserva la pagina ricevuta dal client, si trovano i seguenti elementi:


<!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-02/">
  <link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_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-02] : page serveur, page client</h4>
            </div>
            <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-02/" target="_self" class="nav-link">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page1" target="_self" class="nav-link active nuxt-link-active">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/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" class="alert alert-primary">
                    Page 1 - value = 25
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>
  window.__NUXT__ = (function (a, b, c) {
      return {
        layout: "default", data: [{}], error: null, state: { counter: 25 }, serverRendered: true,
        logs: [
          { date: new Date(1574085336802), args: ["[home beforeCreate]"], type: a, level: b, tag: c },
          { date: new Date(1574085336839), args: ["[home created]"], type: a, level: b, tag: c },
          { date: new Date(1574085336869), args: ["value=", "25"], type: a, level: b, tag: c }
        ]
      }
    }("log", 2, ""));</script>
  <script src="/nuxt-02/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
  • riga 47: il valore inviato dal server;
  • riga 60: si nota che lo stato dello store [Vuex] è stato incorporato nella pagina. Ciò consentirà al client, che verrà eseguito dopo la ricezione della pagina, di ricostituire un nuovo store [Vuex] con 25 come valore iniziale del contatore;

Dopo aver ricevuto e visualizzato la pagina inviata dal server, il client prende il controllo ed esegue a sua volta la pagina [page1]:


...

<script>
/* eslint-disable no-console */

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

export default {
  name: 'Page1',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[page1 beforeCreate]')
  },
  created() {
    // client e server
    console.log('[page1 created]')
    // solo server
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // client e server
    this.value = this.$store.state.counter
    console.log('value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[page1 beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[page1 mounted]')
  }
}
</script>
  • riga 34: la proprietà [value] della riga 18 riceve a sua volta il valore 25 dal contatore;

Lo store di [nuxt] consente quindi al server di trasmettere informazioni al client durante il caricamento iniziale della pagina, quando questa viene richiesta al server. Si ricorda che, una volta ottenuta questa pagina, il server non viene più interpellato e l’applicazione funziona come una classica applicazione [vue], in modalità SAP.

5.3. La pagina [page2]

Nella pagina [page2], mostriamo un altro modo per far sì che:

  • il server includa nella pagina informazioni calcolate;
  • il client non le modifichi;

5.3.1. Il codice della pagina

Il codice della pagina [page2] si evolve come segue:


<!-- pagina 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio -->
    <b-alert slot="right" show variant="secondary"> Page 2 - value = {{ value }} </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',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  asyncData(context) {
    // chi esegue questo codice?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // solo per il server
    if (process.server) {
      // si restituisce una promessa
      return new Promise(function(resolve, reject) {
        // normalmente qui c'è una funzione asincrona
        // la simuliamo con un'attesa di un secondo
        setTimeout(() => {
          // questo risultato sarà incluso nelle proprietà di [data]
          resolve({ value: 87 })
          // log
          console.log('asynData terminée')
        }, 1000)
      })
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[page2 beforeCreate]')
  },
  created() {
    // client e server
    console.log('[page2 created]')
  },
  beforeMount() {
    // solo client
    console.log('[page2 beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[page2 mounted]')
  }
}
</script>
  • riga 7: la pagina visualizza il valore di una proprietà denominata [value];
  • la proprietà [value] non esiste come elemento di un oggetto restituito dalla funzione [data]. In questo caso tale funzione non esiste. La proprietà [value] viene creata dinamicamente dalla riga 36;
  • riga 25: la funzione [asyncData] è una funzione [nuxt]. Come suggerisce il nome, si tratta normalmente di una funzione asincrona. Il suo ruolo abituale è quello di recuperare dati esterni. [nuxt] garantisce che la pagina non venga inviata al browser client prima che la funzione [asyncData] abbia restituito i propri dati asincroni;
  • la funzione [asyncData] riceve come parametro il contesto [nuxt]. Questo oggetto è molto ricco e fornisce accesso a molte informazioni sull’applicazione [Nuxt]. Lo scopriremo nelle sezioni successive;
  • riga 31: si implementa la funzione [asyncData] con un [Promise] (cfr. documento |Introduzione al linguaggio ECMASCRIPT 6 attraverso esempi|). Il costruttore di questa classe accetta come parametro una funzione asincrona che:
    • segnala il successo restituendo i dati tramite la funzione [resolve]. L’oggetto restituito da questa funzione viene automaticamente incluso nelle proprietà [data] della pagina;
    • segnala un errore restituendo un messaggio di errore tramite la funzione [reject];
  • riga 34: si simula una funzione asincrona con la funzione [setTimeout]. Questa funzione restituisce l’oggetto [{ value: 87 }] (riga 36) dopo un secondo (riga 31) grazie alla funzione [resolve] che segnala il successo della funzione [Promise]. L’oggetto restituito dalla funzione asincrona viene incluso automaticamente nelle proprietà [data] della pagina. Ed è quindi questa proprietà che viene visualizzata alla riga 7;
  • riga 27: vedremo che la funzione [asyncData] viene eseguita dal server ma non dal client;
  • riga 29: l’inizializzazione della proprietà [value] viene effettuata dal server;

Nota: l’oggetto [this] non è riconosciuto nella funzione [asyncData] poiché l’oggetto che incapsula il componente [vue] non è ancora stato creato;

5.3.2. Esecuzione

Si esegue il progetto [nuxt-02] e si digita manualmente [localhost:81/nuxt-02/page2] per far sì che il server venga interpellato. Come all’avvio per la pagina [index]:

  • il server esegue la pagina [page2.vue];
  • invia la pagina generata al browser. Questa viene visualizzata;
  • gli script client incorporati nella pagina inviata prendono il controllo ed eseguono nuovamente la pagina [page2.vue];
  • la pagina visualizzata viene quindi modificata;

Il risultato finale è il seguente:

Image

Questa volta, il valore visualizzato è effettivamente quello impostato dal server e, visivamente, non si nota alcun «scatto» della pagina dovuto a una modifica da parte del client del valore visualizzato dal server. Cosa è successo questa volta?

Il server ha eseguito la pagina [page2] seguente:


<!-- pagina 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio -->
    <b-alert slot="right" show variant="secondary"> Page 2 - value = {{ value }} </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',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  asyncData(context) {
    // chi esegue questo codice?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // solo per il server
    if (process.server) {
      // viene restituita una promessa
      return new Promise(function(resolve, reject) {
        // normalmente qui c'è una funzione asincrona
        // la simuliamo con un'attesa di un secondo
        setTimeout(() => {
          // questo risultato sarà incluso nelle proprietà di [data]
          resolve({ value: 87 })
          // log
          console.log('asynData terminée')
        }, 1000)
      })
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[page2 beforeCreate]')
  },
  created() {
    // client e server
    console.log('[page2 created]')
  },
  beforeMount() {
    // solo client
    console.log('[page2 beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[page2 mounted]')
  }
}
</script>

È la riga 36 che ha impostato il valore visualizzato dalla riga 7. Questo è quindi ciò che ha ricevuto il browser client. Più precisamente, riceve la seguente pagina:


<!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-02/">
  <link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_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-02] : page serveur, page client</h4>
            </div>
            <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-02/" target="_self" class="nav-link">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page1" target="_self" class="nav-link">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page2" target="_self" class="nav-link active nuxt-link-active">
                        Page 2
                      </a>
                    </li>
                  </ul>
                </div>
                <div class="col-10">
                  <div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-secondary">
                    Page 2 - value = 87
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>
  window.__NUXT__ = (function (a, b, c) {
      return {
        layout: "default", data: [{ value: 87 }], error: null, state: { counter: 0 }, serverRendered: true,
        logs: [
          { date: new Date(1574096608555), args: ["asyncData, client=", "false", "serveur=", "true"], type: a, level: b, tag: c },
          { date: new Date(1574096608575), args: ["[page2 beforeCreate]"], type: a, level: b, tag: c },
          { date: new Date(1574096608599), args: ["[page2 created]"], type: a, level: b, tag: c }
        ]
      }
    }("log", 2, ""));</script>
  <script src="/nuxt-02/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
  • riga 48: si nota che il valore nella pagina ricevuta è 87;
  • riga 61: nella risposta del server si vedono due oggetti: [data] e [state]:
    • [state] rappresenta lo stato dello store [Vuex]. Quest’ultimo è stato istanziato a partire dal contenuto della cartella [store] dell’applicazione [nuxt-02];
    • [data] contiene le proprietà create dal server tramite la funzione [asyncData]. Troviamo la proprietà [value : 87] creata dal server. Gli script del client integreranno questa proprietà in quelle della pagina [page2];

Torniamo al codice della pagina [page2]:


<!-- pagina 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio -->
    <b-alert slot="right" show variant="secondary"> Page 2 - value = {{ value }} </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',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  asyncData(context) {
    // chi esegue questo codice?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // solo per il server
    if (process.server) {
      // viene restituita una promessa
      return new Promise(function(resolve, reject) {
        // normalmente qui c'è una funzione asincrona
        // la si simula con un'attesa di un secondo
        setTimeout(() => {
          // questo risultato sarà incluso nelle proprietà di [data]
          resolve({ value: 87 })
          // log
          console.log('asynData terminée')
        }, 1000)
      })
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[page2 beforeCreate]')
  },
  created() {
    // client e server
    console.log('[page2 created]')
  },
  beforeMount() {
    // solo client
    console.log('[page2 beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[page2 mounted]')
  }
}
</script>
  • la riga 7 utilizza la proprietà [value]. Tuttavia, la pagina non definisce alcuna proprietà denominata [value]. Gli script del client hanno comunque creato automaticamente questa proprietà grazie all’oggetto [data: [{ value: 87 }]] ricevuto dal server;

I log mostrano inoltre che la funzione [asyncData] non è stata eseguita dal client:

Image

La funzione [asyncData] è stata eseguita dal server [1] ma non dal client [2]. Inoltre, si può notare che le funzioni del ciclo di vita non vengono eseguite dal server prima del completamento della funzione [asyncData]. È possibile aumentare il tempo di attesa all’interno della funzione [asyncData] per verificarlo.

5.4. La pagina [page3]

Aggiungiamo una nuova pagina [page3] alla nostra applicazione:

Image

5.4.1. Il componente [navigation]

Il componente [vavigation] viene modificato per consentire la navigazione verso la nuova pagina:


<template>
  <!-- menu Bootstrap a tre opzioni -->
  <b-nav vertical>
    <b-nav-item to="/" exact exact-active-class="active">
      Home
    </b-nav-item>
    <b-nav-item to="/page1" exact exact-active-class="active">
      Page 1
    </b-nav-item>
    <b-nav-item to="/page2" exact exact-active-class="active">
      Page 2
    </b-nav-item>
    <b-nav-item to="/page3" exact exact-active-class="active">
      Page 3
    </b-nav-item>
  </b-nav>
</template>

5.4.2. Il codice di [page3]

Il codice della pagina [page3] è il seguente:


<!-- pagina 3 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio -->
    <b-alert slot="right" show variant="secondary"> Page 3 - value = {{ value }} </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: 'Page3',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  fetch(context) {
    // chi esegue questo codice?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // solo per il server
    if (process.server) {
      // viene restituita una promessa
      return new Promise(function(resolve, reject) {
        // qui di solito c'è una funzione asincrona
        // la simuliamo con un'attesa di un secondo
        setTimeout(() => {
          // operazione riuscita
          resolve()
        }, 1000)
      }).then(() => {
        // si modifica lo store
        context.store.commit('increment', 28)
      })
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[page3 beforeCreate]')
  },
  created() {
    // client e server
    this.value = this.$store.state.counter
    console.log('[page3 created], value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[page3 beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[page3 mounted]')
  }
}
</script>
  • riga 30: la funzione [fetch] ha un comportamento analogo a quello della funzione [asyncData]:
    • viene eseguita prima delle funzioni del ciclo di vita;
    • l'oggetto [this] non è riconosciuto in questa funzione;
    • il suo funzionamento è asincrono;
    • il ciclo di vita non ha inizio finché la funzione asincrona non ha restituito il proprio risultato;
    • il risultato viene restituito dal metodo [then] della funzione [Promise], riga 43;
    • la funzione [fetch] riceve il parametro [context]. Quest’ultimo rappresenta il contesto [nuxt] del momento;
  • riga 30: tra le sue numerose proprietà, l’oggetto [context] possiede una proprietà [store] che rappresenta lo store [Vuex] dell’applicazione;
  • riga 41: artificialmente, si segnala il successo della [Promise] dopo un secondo (cfr. documento |Introduzione al linguaggio ECMASCRIPT 6 con esempi|);
  • riga 45: viene quindi eseguito il metodo [then]. In esso si incrementa il contatore del [store];

5.4.3. Esecuzione

Si esegue il progetto [nuxt-02] e si digita manualmente [localhost:81/nuxt-02/page3] per inviare una richiesta al server. Come all’avvio per la pagina [index]:

  • il server esegue la pagina [page3.vue];
  • invia la pagina generata al browser. Questa viene visualizzata;
  • gli script client incorporati nella pagina inviata prendono il controllo ed eseguono nuovamente la pagina [page3.vue];
  • la pagina visualizzata viene quindi modificata;

Il risultato finale è il seguente:

Image

Il valore visualizzato è effettivamente quello impostato dal server e, visivamente, non si nota alcun «scatto» della pagina dovuto a una modifica da parte del client del valore visualizzato dal server. Cosa è successo questa volta?

Il server ha eseguito la pagina successiva [page3]:


<!-- pagina 3 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio -->
    <b-alert slot="right" show variant="secondary"> Page 3 - value = {{ value }} </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: 'Page3',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  fetch(context) {
    // chi esegue questo codice?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // solo per il server
    if (process.server) {
      // viene restituita una promessa
      return new Promise(function(resolve, reject) {
        // normalmente qui c'è una funzione asincrona
        // la simuliamo con un'attesa di un secondo
        setTimeout(() => {
          // operazione riuscita
          resolve()
        }, 1000)
      }).then(() => {
        // si modifica lo store
        context.store.commit('increment', 28)
        // log
        console.log('fetch commit terminé')
      })
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[page3 beforeCreate]')
  },
  created() {
    // client e server
    this.value = this.$store.state.counter
    console.log('[page3 created], value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[page3 beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[page3 mounted]')
  }
}
</script>
  • riga 45: la funzione asincrona [fetch] è la prima delle funzioni sopra indicate ad essere eseguita. Riceve come parametro un oggetto denominato [context], che rappresenta il contesto [nuxt] del momento. Tra le numerose proprietà di questo oggetto, la proprietà [context.store] rappresenta lo store [Vuex];
  • riga 45: nella funzione asincrona [fetch], il server imposta il contatore dello store a 28;
  • riga 56: quando viene eseguita la funzione [created], [nuxt] garantisce che la funzione asincrona [fetch] abbia completato il proprio lavoro;
  • riga 58: il valore del contatore della tenda viene assegnato alla proprietà [value] della riga 27;
  • riga 7: visualizzazione del valore di [value], ovvero del contatore dello store;

Il browser del cliente riceve la pagina seguente:


<!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-02/">
  <link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_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-02] : page serveur, page client</h4>
            </div>
            <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-02/" target="_self" class="nav-link">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page1" target="_self" class="nav-link">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page2" target="_self" class="nav-link">
                        Page 2
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page3" target="_self" class="nav-link active nuxt-link-active">
                        Page 3
                      </a>
                    </li>
                  </ul>
                </div> <div class="col-10">
                  <div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-secondary">
                    Page 3 - value = 28
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>
  window.__NUXT__ = (function (a, b, c) {
      return {
        layout: "default", data: [{}], error: null, state: { counter: 28 }, serverRendered: true,
        logs: [
          { date: new Date(1574169916025), args: ["fetch, client=", "false", "serveur=", "true"], type: a, level: b, tag: c },
          { date: new Date(1574169917038), args: ["fetch commit terminé"], type: a, level: b, tag: c },
          { date: new Date(1574169917137), args: ["[page3 beforeCreate]"], type: a, level: b, tag: c },
          { date: new Date(1574169917167), args: ["[page3 created], value=", "28"], type: a, level: b, tag: c }
        ]
      }
    }("log", 2, ""));</script>
  <script src="/nuxt-02/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
  • riga 52: si nota che il valore nella pagina ricevuta è 28;
  • riga 65: nella risposta del server, si vede che il server ha inviato al client lo stato [state] dello store [Vuex]. Grazie a questa informazione, gli script client potranno ricostruire uno store [Vuex];

Gli script client a loro volta eseguiranno il codice della pagina [page3]:


<!-- pagina 3 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio -->
    <b-alert slot="right" show variant="secondary"> Page 3 - value = {{ value }} </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: 'Page3',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  fetch(context) {
    // chi esegue questo codice?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // solo per il server
    if (process.server) {
      // viene restituita una promessa
      return new Promise(function(resolve, reject) {
        // normalmente qui c'è una funzione asincrona
        // la simuliamo con un'attesa di un secondo
        setTimeout(() => {
          // operazione riuscita
          resolve()
        }, 1000)
      }).then(() => {
        // si modifica lo store
        context.store.commit('increment', 28)
        // log
        console.log('fetch commit terminé')
      })
    }
  },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[page3 beforeCreate]')
  },
  created() {
    // client e server
    this.value = this.$store.state.counter
    console.log('[page3 created], value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[page3 beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[page3 mounted]')
  }
}
</script>
  • riga 58: la funzione [created] eseguita dal client assegna il valore del contatore alla proprietà [value] della riga 27;
  • la riga 7 visualizza questo valore. Poiché è lo stesso di quello inviato dal server, non si nota alcun «scatto» della pagina dovuto a una modifica;

I log mostrano inoltre che la funzione [fetch] non è stata eseguita dal client:

Image

La funzione [fetch] è stata eseguita dal server [1] ma non dal client [2]. Inoltre, si può notare che le funzioni del ciclo di vita non vengono eseguite dal server prima del completamento della funzione [fetch] [3]. È possibile aumentare il tempo di attesa all’interno della funzione [fetch] per verificarlo.

Le pagine [page1] e [page3] hanno illustrato due metodi che utilizzano lo store [Vuex] per trasmettere un’informazione dal server al client. Ci si può chiedere se siano equivalenti. Creiamo una pagina [page4] per verificarlo.

5.5. La pagina [page4]

Aggiungiamo una nuova pagina [page4] alla nostra applicazione:

Image

5.5.1. Il componente [navigation]

Il componente [navigation] viene modificato per consentire la navigazione verso la nuova pagina:


<template>
  <!-- menu Bootstrap a cinque opzioni -->
  <b-nav vertical>
    <b-nav-item to="/" exact exact-active-class="active">
      Home
    </b-nav-item>
    <b-nav-item to="/page1" exact exact-active-class="active">
      Page 1
    </b-nav-item>
    <b-nav-item to="/page2" exact exact-active-class="active">
      Page 2
    </b-nav-item>
    <b-nav-item to="/page3" exact exact-active-class="active">
      Page 3
    </b-nav-item>
    <b-nav-item to="/page4" exact exact-active-class="active">
      Page 4
    </b-nav-item>
  </b-nav>
</template>

5.5.2. Il codice di [page4]

Il codice della pagina [page4] è il seguente:


<!-- pagina 4 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio -->
    <b-alert slot="right" show variant="secondary"> Page 4 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */

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

export default {
  name: 'Page4',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo di vita
  async beforeCreate() {
    // client e server
    console.log('[page4 beforeCreate]')
    // solo per il server
    if (process.server) {
      // si esegue la funzione asincrona
      const valeur = await new Promise(function(resolve, reject) {
        // qui di norma si ha una funzione asincrona
        // la si simula con un'attesa di 10 secondi
        setTimeout(() => {
          // operazione riuscita - si restituisce il valore del contatore
          resolve(52)
        }, 10000)
      })
      // si modifica lo store
      this.$store.commit('increment', valeur)
      // log
      console.log('[page4 beforeCreate], fonction asynchrone terminée, compteur=', this.$store.state.counter)
    }
  },
  created() {
    // client e server
    this.value = this.$store.state.counter
    console.log('[page4 created], value=', this.value)
  },
  beforeMount() {
    // solo client
    console.log('[page4 beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[page4 mounted]')
  }
}
</script>
  • riga 30: ciò che prima veniva eseguito nella funzione [fetch] ora viene eseguito nel metodo [beforeCreate]. Si utilizza la coppia async (riga 30) / await (riga 36) per attendere il completamento della funzione asincrona;
  • riga 36: si recupera il risultato della funzione asincrona restituito alla riga 41 dopo 10 secondi (riga 42);
  • righe 50-54: nel metodo [created], eseguito sia sul lato server che sul lato client, il contatore viene assegnato alla proprietà [value] della pagina;

5.5.3. Esecuzione

Si esegue il progetto [nuxt-02] e si digita manualmente [localhost:81/nuxt-02/page4] per inviare una richiesta al server. Come all’avvio della pagina [index]:

  • il server esegue la pagina [page4.vue];
  • invia la pagina generata al browser. Questa viene visualizzata;
  • gli script client incorporati nella pagina inviata prendono il controllo ed eseguono nuovamente la pagina [page4.vue];
  • la pagina visualizzata viene quindi modificata;

Il risultato finale è il seguente:

Image

Contrariamente a quanto previsto, il valore visualizzato in [2] non è 52. Cosa è successo?

I log sono i seguenti:

Image

Si può notare che in [1] il log di fine dell’azione asincrona non è stato visualizzato. La funzione [created], che visualizza il valore del contatore, mostra 0. Tutto ciò fa supporre che [nuxt] non abbia atteso il completamento dell’azione asincrona.

Se si torna al terminale di VSCode utilizzato per l’avvio dell’applicazione, si trovano i log di [3-4]. Si nota che la funzione asincrona è stata effettivamente eseguita sul lato server.

In definitiva, la funzione [beforeCreate] è stata effettivamente eseguita per intero sul lato server, ma [nuxt] non ha atteso il completamento della sua esecuzione per inviare la pagina al browser client, mentre attende invece il completamento della funzione [fetch]. È quindi questo il metodo da utilizzare se si desidera che il server inizializzi uno store [Vuex].

5.6. Navigazione nell’applicazione [vue]

Abbiamo illustrato cosa accadeva quando ciascuna delle pagine [index, page1, page2, page3, page4] veniva caricata inizialmente dal server. In pratica, però, non è questo ciò che accade: in condizioni di funzionamento normale, sul server viene ricercata solo la pagina [index]. Esaminiamo le tre pagine in questo caso:

pagina [index]

Image

Abbiamo già spiegato questo risultato nel paragrafo dedicato ai link.

Ora clicchiamo sul link [Page 1]:

Image

Il valore visualizzato è 0. Era 25 quando la pagina è stata richiesta per la prima volta al server digitando manualmente il suo URL. La spiegazione è semplice. Il codice eseguito è il seguente:


  created() {
    // client e server
    console.log('[page1 created]')
    // solo server
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // client e server
    this.value = this.$store.state.counter
    console.log('value=', this.value)
},

È la riga 6 che impostava il contatore a 25. Poiché la pagina non è stata richiesta al server, le righe 5-7 non sono state eseguite e il contatore dello store [Vuex] è rimasto a 0.

Ora clicchiamo sul link [Page 2]:

Image

Questa volta non viene visualizzato alcun valore e, inoltre, nei log della console compare un avviso:

Image

  • In [1], si scopre che la funzione [asyncData] è stata eseguita dal client. È sempre così:
    • viene eseguita dal server se la pagina viene richiesta al server; in questo caso, non viene eseguita dal client;
    • poi ogni volta che la pagina è la destinazione del percorso corrente del client;
  • in [2]: [nuxt] emette un avviso perché nel template della pagina è presente un'espressione reattiva {{ value }} mentre la pagina non possiede la proprietà [value];

Ricordiamo il codice eseguito dal client:


asyncData() {
    // chi esegue questo codice?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // solo per il server
    if (process.server) {
      // viene restituita una promessa
      return new Promise(function(resolve, reject) {
        // normalmente qui ci sarebbe una funzione asincrona, ma non in questo caso
        // questo risultato sarà incluso nelle proprietà di [data]
        resolve({ value: 87 })
      })
    }
  },
  • la riga 3 ha permesso di vedere che la funzione [asyncData] è stata eseguita dal client prima ancora delle funzioni del ciclo di vita;
  • la riga 5 ha impedito l’esecuzione del resto del codice che creava la proprietà [value] poiché tale codice viene eseguito dal client;

Passiamo ora alla pagina [page3]:

Image

  • in [2], il valore era 28 quando la pagina veniva fornita dal server. Qui non è così;
  • in [4], si vede che la funzione [fetch] è stata eseguita sul lato client;

Esaminiamo il codice eseguito dal client:


...
fetch(context) {
    // chi esegue questo codice?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // solo per il server
    if (process.server) {
      // viene restituita una promessa
      return new Promise(function(resolve, reject) {
        // normalmente qui si ha una funzione asincrona
        // la simuliamo con un'attesa di un secondo
        setTimeout(() => {
          // operazione riuscita
          resolve()
        }, 1000)
      }).then(() => {
        // si modifica lo store
        context.store.commit('increment', 28)
        // log
        console.log('fetch commit terminé')
      })
    }
},
...
  • il client esegue il metodo [fetch], riga 2;
  • le righe da 6 a 21 non vengono eseguite poiché la condizione [process.server] è falsa. Pertanto, la riga 17 che imposta il contatore a 28 non viene eseguita. Il contatore rimane a zero. Ecco perché il client visualizza 0 invece di 28;

Passiamo ora alla pagina [page4]. Si ottiene il seguente risultato:

Image

  • in [2], il valore del contatore;
  • in [3], i log del client;

Il codice eseguito dal client è il seguente:


...
data() {
    return {
      value: 0
    }
  },
  // ciclo di vita
  async beforeCreate() {
    // client e server
    console.log('[page4 beforeCreate]')
    // solo per il server
    if (process.server) {
      // si esegue la funzione asincrona
      const valeur = await new Promise(function(resolve, reject) {
        // qui si ha normalmente una funzione asincrona
        // la si simula con un'attesa di 10 secondi
        setTimeout(() => {
          // Operazione riuscita: si restituisce il valore del contatore
          resolve(52)
        }, 10000)
      })
      // si modifica lo store
      this.$store.commit('increment', valeur)
      // log
      console.log('[page4 beforeCreate], fonction asynchrone terminée, compteur=', this.$store.state.counter)
    }
  },
  created() {
    // client e server
    this.value = this.$store.state.counter
    console.log('[page4 created], value=', this.value)
  },
...
  • le righe da 12 a 26 non vengono eseguite dal client, poiché la condizione [process.server] della riga 12 è falsa. Pertanto, il contatore dello store [Vuex] è pari a 0 (il suo valore iniziale nello store) ed è proprio questo valore che viene visualizzato sulla pagina;

Ci si potrebbe chiedere cosa succede quando si commentano le righe 12 e 26 ([if]). Ecco la risposta:

  • questa volta il client esegue le righe 14-25, ma [nuxt] non attende la fine della funzione asincrona (come nel caso del server) e lascia quindi il contatore a 0;
  • dopo 10 secondi, la funzione asincrona termina e il contatore viene impostato a 52 alla riga 23;
  • quando si torna alla pagina [page4], viene visualizzato il valore 52;

5.7. Résumé

Dai nostri vari test, si possono trarre le seguenti conclusioni:

  • se la pagina [index] deve contenere dati esterni, questi possono essere recuperati dal server tramite la funzione [asyncData];
  • se il server deve inizializzare uno store [Vuex] con dati esterni al momento del caricamento della pagina [index], lo farà nella funzione [fetch];
  • la pagina generata dal server e quella generata dal client devono essere identiche se si vuole evitare l’effetto di “scatto” causato dal fatto che la pagina del client sostituisce visivamente quella inviata dal server e inizialmente visualizzata;

Scopriremo altri aspetti di [nuxt] attraverso un nuovo esempio.