Skip to content

5. Voorbeeld [nuxt-02]: server- en clientpagina’s

In dit project laten we zien:

  • dat de door de client opgebouwde pagina er visueel anders uit kan zien dan de pagina die van de server is ontvangen. Dit leidt tot een snelle paginawisseling die voor de gebruiker waarneembaar is en dus schadelijk is voor de gebruiksvriendelijkheid van de applicatie. Dit is daarom een optie die moet worden vermeden;
  • een oplossing waarbij de clientpagina dezelfde pagina nabootst als die welke door de server is verzonden;

Het project [nuxt-02] is in eerste instantie verkregen door het project [nuxt-01] te kopiëren.

Image

Er wordt een map [store] aan het project toegevoegd, evenals twee nieuwe pagina’s. We komen hier later op terug.

5.1. De pagina [index]

5.1.1. De code van de pagina

De code van de pagina [index] ziet er nu als volgt uit:


<!-- hoofdpagina -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigatie -->
    <Navigation slot="left" />
    <!-- bericht-->
    <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',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[home beforeCreate]')
  },
  created() {
    // client en server
    console.log('[home created]')
    // alleen server
    if (process.server) {
      this.value = 10
    }
    // client en server
    console.log('value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[home beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[home mounted]')
  }
}
</script>

Opmerkingen

  • regel 7: de pagina [index] geeft de waarde van de eigenschap [value] weer (regel 28);
  • regels 36-45: houd er hier rekening mee dat de functie [created] zowel aan de serverzijde als aan de clientzijde wordt uitgevoerd. In de regels 40-42 stelt de server de waarde van de eigenschap [value] in op 10. De client raakt deze waarde echter niet aan. We willen gewoon weten of deze waarde door de client wordt behouden. We zullen ontdekken dat dit niet het geval is;

5.1.2. Uitvoering

We passen het bestand [/nuxt.config.js] aan om het project [nuxt-02] uit te voeren:


...
  // broncodemap
  srcDir: 'nuxt-02',
  // router
  router: {
    // hoofdmap van de applicatie URL
    base: '/nuxt-02/'
  },
  // server
  server: {
    // servicepoort, standaard 3000
    port: 81,
    // netwerkadressen waarop wordt geluisterd, standaard localhost: 127.0.0.1
    // 0.0.0.0 = alle netwerkadressen van de machine
    host: 'localhost'
  }
...

We voeren het project [1] uit:

Image

De pagina [index] wordt vervolgens weergegeven als [2-3]. Deze toont even de waarde [10] en vervolgens de waarde [0]. Wat is er gebeurd?

stap 1

De server wordt als eerste uitgevoerd. Deze voert de code van de pagina [index] uit:


export default {
  name: 'Home',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[home beforeCreate]')
  },
  created() {
    // client en server
    console.log('[home created]')
    // alleen server
    if (process.server) {
      this.value = 10
    }
    // client en server
    console.log('value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[home beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[home mounted]')
  }
}
  • door regel 23 krijgt de eigenschap [value] van regel 10 de waarde 10;

Dit kan worden gecontroleerd door de broncode te bekijken van de pagina die door de browser wordt ontvangen (optie [code source] in de 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>
  • regel 46: op de ontvangen pagina had [value] de waarde 10;

stap 2

We weten dat na ontvangst van de pagina de scripts op de regels 57-60 het overnemen en het gedrag van de ontvangen pagina wijzigen, of zelfs de weergegeven informatie, zoals hier. Deze scripts vormen de client die op zijn beurt de code van de pagina [index] uitvoert, dezelfde code als de server:


export default {
  name: 'Home',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[home beforeCreate]')
  },
  created() {
    // client en server
    console.log('[home created]')
    // alleen server
    if (process.server) {
      this.value = 10
    }
    // client en server
    console.log('value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[home beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[home mounted]')
  }
}
  • Om te begrijpen wat er gebeurt, moet je beseffen dat de client [nuxt] de regels 22-24 niet zal uitvoeren (process.server=false);
  • in een klassieke [vue]-toepassing blijft de eigenschap [value] van regel 10 op 0 staan. Daarom wordt de weergegeven waarde, zodra de client naar de ontvangen pagina is overgeschakeld, [0];

De door de server gegenereerde waarde [nuxt] voor de eigenschap [value] heeft geen nut gehad.

5.2. De pagina [page1]

5.2.1. De store [Vuex]

We hebben een map [store] toegevoegd aan het project [nuxt-02]:

Image

Door de aanwezigheid van dit bestand zal [nuxt] automatisch een store [Vuex] implementeren. Het is het bestand [index.js] dat deze store implementeert. Hier is het bestand [index.js] als volgt:


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

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

[nuxt] implementeert een store [Vuex] op basis van de inhoud van [index.js]:

  • regels 1-3: definitie van de status [state] van de store. Deze status wordt door een functie geretourneerd. Hier heeft de status slechts één eigenschap, de teller op regel 2. De geëxporteerde functie moet [state] heten;
  • regels 5-9: de mogelijke bewerkingen op de status van de store. Deze worden [mutations] genoemd. Hier maakt de mutatie [increment] het mogelijk om de eigenschap [counter] met een hoeveelheid [inc] te verhogen. Het geëxporteerde object moet [mutations] heten;

De [store]-weergave, geïmplementeerd door [nuxt], is op verschillende plaatsen beschikbaar. In de weergaven is deze beschikbaar in de eigenschap [this.$store].

5.2.2. De code van de pagina

Net als de pagina [index] zal de pagina [page1] een waarde weergeven, namelijk die van de teller van de Vuex-store:


<!-- pagina 1 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigatie -->
    <Navigation slot="left" />
    <!-- bericht-->
    <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',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[home beforeCreate]')
  },
  created() {
    // client en server
    console.log('[home created]')
    // alleen server
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // client en server
    this.value = this.$store.state.counter
    console.log('value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[home beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[home mounted]')
  }
}
</script>

Opmerkingen

  • regels 38-40: de server verhoogt de teller met 25;
  • regel 42: zowel de server als de client geven de waarde van de teller weer;
  • regel 7: de waarde van de teller wordt weergegeven;

Bij het lezen van deze code moet u twee dingen begrijpen:

  • de uitgevoerde code is voor de server dezelfde als voor de client;
  • het object [this] is echter niet hetzelfde: er is een versie [this] aan de serverzijde en een andere aan de clientzijde, namelijk [client];

We willen weten of de [this.$store] van de server dezelfde is als de [this.$store] van de client. Aangezien de server als eerste wordt uitgevoerd (bij het opstarten van de applicatie), komt dit neer op de vraag: wordt de door de server geïnitialiseerde [store] doorgegeven aan de client?

5.2.3. Uitvoering

We voeren het project [nuxt-02] uit en typen handmatig [localhost:81/nuxt-02/page1] in, zodat de server wordt aangeroepen. Net als bij het opstarten voor de pagina [index]:

  • voert de server de pagina [page1.vue] uit;
  • stuurt de gegenereerde pagina naar de browser. Deze wordt weergegeven;
  • de in de verzonden pagina ingebedde clientscripts nemen het over en voeren de pagina [page1.vue] opnieuw uit;
  • de weergegeven pagina wordt vervolgens gewijzigd;

Het eindresultaat is als volgt:

Image

Deze keer is de weergegeven waarde inderdaad de waarde die door de server is vastgesteld en visueel is er geen ‘sprong’ in de pagina te zien als gevolg van een wijziging door de client van de door de server weergegeven waarde. Wat is er deze keer gebeurd?

De server heeft de volgende pagina [page1] uitgevoerd:


...

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

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

export default {
  name: 'Page1',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[page1 beforeCreate]')
  },
  created() {
    // client en server
    console.log('[page1 created]')
    // alleen server
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // client en server
    this.value = this.$store.state.counter
    console.log('value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[page1 beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[page1 mounted]')
  }
}
</script>
  • de regels 30-32 zijn zonder fouten uitgevoerd. Dit betekent dat ook aan de serverzijde [this.$store] verwijst naar de store [Vuex]. Regel 31 heeft de teller van de store op 25 gezet;
  • daarna is de pagina naar de klant verzonden;

Als we de door de client ontvangen pagina bekijken, vinden we de volgende elementen:


<!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>
  • regel 47: de door de server verzonden waarde;
  • regel 60: we zien dat de status van de store [Vuex] in de pagina is opgenomen. Hierdoor kan de client, die na ontvangst van de pagina wordt uitgevoerd, een nieuwe store [Vuex] aanmaken met 25 als beginwaarde van de teller;

Na ontvangst en weergave van de van de server ontvangen pagina neemt de client het over en voert op zijn beurt de pagina [page1] uit:


...

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

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

export default {
  name: 'Page1',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[page1 beforeCreate]')
  },
  created() {
    // client en server
    console.log('[page1 created]')
    // alleen server
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // client en server
    this.value = this.$store.state.counter
    console.log('value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[page1 beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[page1 mounted]')
  }
}
</script>
  • regel 34: de eigenschap [value] van regel 18 krijgt op zijn beurt de waarde 25 van de teller;

De store van [nuxt] stelt de server dus in staat om informatie naar de client te verzenden tijdens het eerste laden van de pagina, wanneer deze op de server wordt opgehaald. Ter herinnering: zodra deze pagina is opgehaald, wordt de server niet meer benaderd en werkt de applicatie als een klassieke [vue]-applicatie, in de modus SAP.

5.3. De pagina [page2]

Op de pagina [page2] laten we een andere manier zien waarop:

  • de server berekende informatie in de pagina opneemt;
  • de client deze informatie niet wijzigt;

5.3.1. De code van de pagina

De code van de pagina [page2] verandert als volgt:


<!-- pagina 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigatie -->
    <Navigation slot="left" />
    <!-- bericht -->
    <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',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  asyncData(context) {
    // wie voert deze code uit?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // alleen voor de server
    if (process.server) {
      // er wordt een promise geretourneerd
      return new Promise(function(resolve, reject) {
        // hier hebben we normaal gesproken een asynchrone functie
        // we simuleren deze met een wachttijd van één seconde
        setTimeout(() => {
          // dit resultaat wordt opgenomen in de eigenschappen van [data]
          resolve({ value: 87 })
          // log
          console.log('asynData terminée')
        }, 1000)
      })
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[page2 beforeCreate]')
  },
  created() {
    // client en server
    console.log('[page2 created]')
  },
  beforeMount() {
    // alleen client
    console.log('[page2 beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[page2 mounted]')
  }
}
</script>
  • regel 7: de pagina geeft de waarde weer van een eigenschap met de naam [value];
  • de eigenschap [value] bestaat niet als onderdeel van een object dat wordt gerenderd door de functie [data]. Deze functie bestaat hier niet. De eigenschap [value] wordt dynamisch aangemaakt door regel 36;
  • regel 25: de functie [asyncData] is een functie [nuxt]. Zoals de naam al aangeeft, is dit normaal gesproken een asynchrone functie. De gebruikelijke rol ervan is het ophalen van externe gegevens. [nuxt] zorgt ervoor dat de pagina niet naar de clientbrowser wordt verzonden voordat de functie [asyncData] haar asynchrone gegevens heeft gerenderd;
  • de functie [asyncData] ontvangt als parameter de context [nuxt]. Dit object is zeer uitgebreid en biedt toegang tot veel informatie over de Nuxt-applicatie. We zullen dit in de volgende paragrafen ontdekken;
  • regel 31: we implementeren de functie [asyncData] met een [Promise] (zie document |Inleiding tot de taal ECMASCRIPT 6 aan de hand van een voorbeeld|). De constructor van deze klasse accepteert als parameter een asynchrone functie die:
    • succes meldt door gegevens terug te geven met de functie [resolve]. Het object dat door deze functie wordt teruggegeven, wordt automatisch opgenomen in de eigenschappen [data] van de pagina;
    • een mislukking signaleert door een fout terug te geven met de functie [reject];
  • regel 34: er wordt een asynchrone functie gesimuleerd met de functie [setTimeout]. Deze functie retourneert het object [{ value: 87 }] (regel 36) na één seconde (regel 31) dankzij de functie [resolve], die aangeeft dat [Promise] succesvol is uitgevoerd. Het object dat door de asynchrone functie wordt geretourneerd, wordt automatisch opgenomen in de eigenschap [data] van de pagina. En het is dus deze eigenschap die op regel 7 wordt weergegeven;
  • regel 27: we zullen zien dat de functie [asyncData] door de server wordt uitgevoerd, maar niet door de client;
  • regel 29: de initialisatie van de eigenschap [value] gebeurt door de server;

Opmerking: het object [this] is niet bekend in de functie [asyncData], omdat het object dat de component [vue] omvat nog niet is aangemaakt;

5.3.2. Uitvoering

We voeren het project [nuxt-02] uit en typen handmatig [localhost:81/nuxt-02/page2] in om de server aan te roepen. Net als bij het opstarten voor de pagina [index]:

  • voert de server de pagina [page2.vue] uit;
  • stuurt de gegenereerde pagina naar de browser. Deze wordt weergegeven;
  • de in de verzonden pagina ingebedde clientscripts nemen het over en voeren de pagina [page2.vue] opnieuw uit;
  • de weergegeven pagina wordt vervolgens gewijzigd;

Het eindresultaat is als volgt:

Image

Deze keer is de weergegeven waarde inderdaad de waarde die door de server is vastgesteld en visueel is er geen ‘sprong’ in de pagina te zien als gevolg van een wijziging door de client van de door de server weergegeven waarde. Wat is er deze keer gebeurd?

De server heeft de volgende pagina [page2] uitgevoerd:


<!-- pagina 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigatie -->
    <Navigation slot="left" />
    <!-- bericht -->
    <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',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  asyncData(context) {
    // wie voert deze code uit?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // alleen voor de server
    if (process.server) {
      // er wordt een promise geretourneerd
      return new Promise(function(resolve, reject) {
        // hier hebben we normaal gesproken een asynchrone functie
        // we simuleren deze met een wachttijd van één seconde
        setTimeout(() => {
          // dit resultaat wordt opgenomen in de eigenschappen van [data]
          resolve({ value: 87 })
          // log
          console.log('asynData terminée')
        }, 1000)
      })
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[page2 beforeCreate]')
  },
  created() {
    // client en server
    console.log('[page2 created]')
  },
  beforeMount() {
    // alleen client
    console.log('[page2 beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[page2 mounted]')
  }
}
</script>

Het is regel 36 die de waarde heeft vastgesteld die door regel 7 wordt weergegeven. Dit is dus wat de clientbrowser heeft ontvangen. Om precies te zijn ontvangt deze de volgende 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>
  • regel 48: we zien dat de waarde in de ontvangen pagina 87 is;
  • regel 61: in het antwoord van de server zien we twee objecten: [data] en [state]:
    • [state] is de status van de store [Vuex]. Deze is geïnstantieerd op basis van de inhoud van de map [store] van de applicatie [nuxt-02];
    • [data] bevat de eigenschappen die door de server zijn aangemaakt met behulp van de functie [asyncData]. We zien hier de eigenschap [value : 87] die door de server is aangemaakt. De scripts van de client zullen deze eigenschap integreren in die van de pagina [page2];

Laten we teruggaan naar de code van de pagina [page2]:


<!-- pagina 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigatie -->
    <Navigation slot="left" />
    <!-- bericht -->
    <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',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  asyncData(context) {
    // wie voert deze code uit?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // alleen voor de server
    if (process.server) {
      // er wordt een promise geretourneerd
      return new Promise(function(resolve, reject) {
        // hier hebben we normaal gesproken een asynchrone functie
        // we simuleren deze met een wachttijd van één seconde
        setTimeout(() => {
          // dit resultaat wordt opgenomen in de eigenschappen van [data]
          resolve({ value: 87 })
          // log
          console.log('asynData terminée')
        }, 1000)
      })
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[page2 beforeCreate]')
  },
  created() {
    // client en server
    console.log('[page2 created]')
  },
  beforeMount() {
    // alleen client
    console.log('[page2 beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[page2 mounted]')
  }
}
</script>
  • op regel 7 wordt de eigenschap [value] gebruikt. De pagina definieert echter geen eigenschap met de naam [value]. De scripts van de client hebben deze eigenschap echter automatisch aangemaakt dankzij het object [data: [{ value: 87 }]] dat van de server is ontvangen;

Uit de logbestanden blijkt bovendien dat de functie [asyncData] niet door de client is uitgevoerd:

Image

De functie [asyncData] is uitgevoerd door de server [1], maar niet door de client [2]. Bovendien valt op dat de levenscyclusfuncties niet door de server worden uitgevoerd voordat de functie [asyncData] is voltooid. Om dit te controleren, kan de wachttijd binnen de functie [asyncData] worden verlengd.

5.4. De pagina [page3]

We voegen een nieuwe pagina [page3] toe aan onze applicatie:

Image

5.4.1. De component [navigation]

De component [vavigation] wordt aangepast om navigatie naar de nieuwe pagina mogelijk te maken:


<template>
  <!-- Bootstrap-menu met drie opties -->
  <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. De code van [page3]

De code van de pagina [page3] is als volgt:


<!-- pagina 3 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigatie -->
    <Navigation slot="left" />
    <!-- bericht -->
    <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',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  fetch(context) {
    // wie voert deze code uit?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // alleen voor de server
    if (process.server) {
      // er wordt een belofte geretourneerd
      return new Promise(function(resolve, reject) {
        // hier hebben we normaal gesproken een asynchrone functie
        // we simuleren deze met een wachttijd van één seconde
        setTimeout(() => {
          // geslaagd
          resolve()
        }, 1000)
      }).then(() => {
        // de store wordt gewijzigd
        context.store.commit('increment', 28)
      })
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[page3 beforeCreate]')
  },
  created() {
    // client en server
    this.value = this.$store.state.counter
    console.log('[page3 created], value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[page3 beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[page3 mounted]')
  }
}
</script>
  • regel 30: de functie [fetch] gedraagt zich op dezelfde manier als de functie [asyncData]:
    • deze wordt uitgevoerd vóór de functies van de levenscyclus;
    • het object [this] is in deze functie niet bekend;
    • de werking ervan is asynchroon;
    • de levenscyclus begint pas zodra de asynchrone functie haar resultaat heeft geretourneerd;
    • het resultaat wordt hier geretourneerd door de methode [then] van [Promise], regel 43;
    • de functie [fetch] ontvangt de parameter [context]. Deze vertegenwoordigt de huidige context [nuxt];
  • regel 30: naast zijn vele eigenschappen heeft het object [context] een eigenschap [store] die de store [Vuex] van de applicatie vertegenwoordigt;
  • regel 41: kunstmatig wordt na één seconde gemeld dat de [Promise] is geslaagd (zie document |Inleiding tot de taal ECMASCRIPT 6 aan de hand van een voorbeeld|);
  • regel 45: de methode [then] wordt vervolgens uitgevoerd. Daarin wordt de teller van de [store] verhoogd;

5.4.3. Uitvoering

We voeren het project [nuxt-02] uit en typen handmatig [localhost:81/nuxt-02/page3] in om de server aan te roepen. Net als bij het opstarten voor de pagina [index]:

  • voert de server de pagina [page3.vue] uit;
  • stuurt de gegenereerde pagina naar de browser. Deze wordt weergegeven;
  • de in de verzonden pagina ingebedde clientscripts nemen het over en voeren de pagina [page3.vue] opnieuw uit;
  • de weergegeven pagina wordt vervolgens gewijzigd;

Het eindresultaat is als volgt:

Image

De weergegeven waarde is inderdaad de waarde die door de server is vastgesteld en visueel is er geen ‘sprong’ in de pagina te zien als gevolg van een wijziging door de client van de door de server weergegeven waarde. Wat is er deze keer gebeurd?

De server heeft de volgende pagina [page3] uitgevoerd:


<!-- pagina 3 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigatie -->
    <Navigation slot="left" />
    <!-- bericht -->
    <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',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  fetch(context) {
    // wie voert deze code uit?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // alleen voor de server
    if (process.server) {
      // er wordt een promise geretourneerd
      return new Promise(function(resolve, reject) {
        // hier hebben we normaal gesproken een asynchrone functie
        // we simuleren deze met een wachttijd van één seconde
        setTimeout(() => {
          // geslaagd
          resolve()
        }, 1000)
      }).then(() => {
        // de store wordt gewijzigd
        context.store.commit('increment', 28)
        // log
        console.log('fetch commit terminé')
      })
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[page3 beforeCreate]')
  },
  created() {
    // client en server
    this.value = this.$store.state.counter
    console.log('[page3 created], value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[page3 beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[page3 mounted]')
  }
}
</script>
  • regel 45: de asynchrone functie [fetch] is de eerste van de bovenstaande functies die wordt uitgevoerd. Deze ontvangt als parameter een object met de naam [context], wat de huidige context [nuxt] is. Van de zeer vele eigenschappen van dit object vertegenwoordigt de eigenschap [context.store] de store [Vuex];
  • regel 45: in de asynchrone functie [fetch] stelt de server de teller van de store in op 28;
  • regel 56: wanneer de functie [created] wordt uitgevoerd, zorgt [nuxt] ervoor dat de asynchrone functie [fetch] haar werk heeft voltooid;
  • regel 58: de waarde van de teller van de zonwering wordt toegewezen aan de eigenschap [value] van regel 27;
  • regel 7: weergave van de waarde van [value], dus van de teller van de store;

De browser van de klant ontvangt de volgende 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">
                        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>
  • regel 52: we zien dat de waarde op de ontvangen pagina 28 is;
  • regel 65: in het antwoord van de server zien we dat de server de status [state] van de store [Vuex] naar de client heeft verzonden. Dankzij deze informatie kunnen de clientscripts een store [Vuex] reconstrueren;

De clientscripts zullen op hun beurt de code van de pagina [page3] uitvoeren:


<!-- pagina 3 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigatie -->
    <Navigation slot="left" />
    <!-- bericht -->
    <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',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  fetch(context) {
    // wie voert deze code uit?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // alleen voor de server
    if (process.server) {
      // er wordt een promise geretourneerd
      return new Promise(function(resolve, reject) {
        // hier hebben we normaal gesproken een asynchrone functie
        // we simuleren deze met een wachttijd van één seconde
        setTimeout(() => {
          // geslaagd
          resolve()
        }, 1000)
      }).then(() => {
        // de store wordt gewijzigd
        context.store.commit('increment', 28)
        // log
        console.log('fetch commit terminé')
      })
    }
  },
  // levenscyclus
  beforeCreate() {
    // client en server
    console.log('[page3 beforeCreate]')
  },
  created() {
    // client en server
    this.value = this.$store.state.counter
    console.log('[page3 created], value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[page3 beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[page3 mounted]')
  }
}
</script>
  • regel 58: de functie [created], uitgevoerd door de client, wijst de waarde van de teller toe aan de eigenschap [value] op regel 27;
  • regel 7 geeft deze waarde weer. Aangezien dit dezelfde waarde is als die door de server is verzonden, zien we de pagina niet ‘springen’ als gevolg van een wijziging;

Uit de logbestanden blijkt bovendien dat de functie [fetch] niet door de client is uitgevoerd:

Image

De functie [fetch] is uitgevoerd door de server [1], maar niet door de client [2]. Bovendien valt op dat de levenscyclusfuncties niet door de server worden uitgevoerd voordat de functie [fetch] [3] is voltooid. Om dit te controleren, kan de wachttijd binnen de functie [fetch] worden verlengd.

De pagina’s [page1] en [page3] lieten twee methoden zien waarbij de store [Vuex] wordt gebruikt om informatie van de server naar de client door te geven. Men kan zich afvragen of deze methoden gelijkwaardig zijn. We maken een pagina [page4] om dit te controleren.

5.5. De pagina [page4]

We voegen een nieuwe pagina [page4] toe aan onze applicatie:

Image

5.5.1. De component [navigation]

De component [navigation] wordt aangepast om navigatie naar de nieuwe pagina mogelijk te maken:


<template>
  <!-- Bootstrap-menu met vijf opties -->
  <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. De code van [page4]

De code van de pagina [page4] is als volgt:


<!-- pagina 4 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigatie -->
    <Navigation slot="left" />
    <!-- bericht -->
    <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',
  // gebruikte componenten
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // levenscyclus
  async beforeCreate() {
    // client en server
    console.log('[page4 beforeCreate]')
    // alleen voor de server
    if (process.server) {
      // de asynchrone functie wordt uitgevoerd
      const valeur = await new Promise(function(resolve, reject) {
        // hier is normaal gesproken sprake van een asynchrone functie
        // deze wordt gesimuleerd met een wachttijd van 10 seconden
        setTimeout(() => {
          // geslaagd – de waarde van de teller wordt teruggegeven
          resolve(52)
        }, 10000)
      })
      // de store wordt gewijzigd
      this.$store.commit('increment', valeur)
      // log
      console.log('[page4 beforeCreate], fonction asynchrone terminée, compteur=', this.$store.state.counter)
    }
  },
  created() {
    // client en server
    this.value = this.$store.state.counter
    console.log('[page4 created], value=', this.value)
  },
  beforeMount() {
    // alleen client
    console.log('[page4 beforeMount]')
  },
  mounted() {
    // alleen client
    console.log('[page4 mounted]')
  }
}
</script>
  • regel 30: wat voorheen in de functie [fetch] werd gedaan, gebeurt nu in de methode [beforeCreate]. We gebruiken het duo async (regel 30) / await (regel 36) om te wachten tot de asynchrone functie is voltooid;
  • regel 36: het resultaat van de asynchrone functie wordt na 10 seconden (regel 42) opgehaald op regel 41;
  • regels 50-54: in de methode [created], die zowel aan de serverzijde als aan de clientzijde wordt uitgevoerd, wordt de teller toegewezen aan de eigenschap [value] van de pagina;

5.5.3. Uitvoering

We voeren het project [nuxt-02] uit en typen handmatig [localhost:81/nuxt-02/page4] in om de server aan te roepen. Net als bij het opstarten van de pagina [index]:

  • voert de server de pagina [page4.vue] uit;
  • stuurt de gegenereerde pagina naar de browser. Deze wordt weergegeven;
  • de in de verzonden pagina ingebedde clientscripts nemen het over en voeren de pagina [page4.vue] opnieuw uit;
  • de weergegeven pagina wordt vervolgens gewijzigd;

Het eindresultaat is als volgt:

Image

In tegenstelling tot wat verwacht werd, is de weergegeven waarde in [2] niet 52. Wat is er gebeurd?

De logbestanden zien er als volgt uit:

Image

We zien dat in [1] het logboek van het einde van de asynchrone actie niet is weergegeven. De functie [created], die de waarde van de teller weergeeft, geeft 0 weer. Dit alles wijst erop dat [nuxt] niet heeft gewacht tot de asynchrone actie was voltooid.

Als we teruggaan naar de terminal van VSCode, die is gebruikt om de applicatie te starten, vinden we de logboeken van [3-4]. We zien dat de asynchrone functie wel degelijk aan de serverzijde is uitgevoerd.

Uiteindelijk is de functie [beforeCreate] wel volledig aan de serverzijde uitgevoerd, maar [nuxt] heeft niet gewacht tot de uitvoering ervan was voltooid om de pagina naar de clientbrowser te verzenden, terwijl het wel wacht op het einde van de functie [fetch]. Dit is dus de methode die je moet gebruiken als je wilt dat de server een store [Vuex] initialiseert.

5.6. Navigatie in de applicatie [vue]

We hebben laten zien wat er gebeurde toen elk van de [index, page1, page2, page3, page4]-pagina’s aanvankelijk door de server werd geladen. In de praktijk gebeurt dit niet: bij normaal gebruik wordt alleen de pagina [index] op de server opgehaald. Laten we in dit geval de drie pagina’s eens bekijken:

pagina [index]

Image

We hebben dit resultaat al uitgelegd in de paragraaf over links.

Laten we nu op de link [Page 1] klikken:

Image

De weergegeven waarde is 0. Deze was 25 toen de pagina voor het eerst bij de server werd opgevraagd door de URL URL handmatig in te voeren. De verklaring is eenvoudig. De uitgevoerde code is als volgt:


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

Het was regel 6 die de teller op 25 zette. Aangezien de pagina niet bij de server werd opgevraagd, werden de regels 5-7 niet uitgevoerd en bleef de teller van de store [Vuex] op 0 staan.

Laten we nu op de link [Page 2] klikken:

Image

Deze keer wordt er geen waarde weergegeven en verschijnt er bovendien een waarschuwing in de logbestanden van de console:

Image

  • In [1] zien we dat de functie [asyncData] door de client is uitgevoerd. Dit is altijd het geval:
    • de functie wordt door de server uitgevoerd als de pagina bij de server wordt opgevraagd. In dat geval wordt de functie niet door de client uitgevoerd;
    • en vervolgens telkens wanneer de pagina het doel is van de huidige route van de client;
  • in [2]: [nuxt] geeft een waarschuwing omdat er in de sjabloon van de pagina een reactieve uitdrukking {{ value }} staat, terwijl de pagina geen eigenschap [value] heeft;

Laten we nog eens kijken naar de code die door de client wordt uitgevoerd:


asyncData() {
    // wie voert deze code uit?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // alleen voor de server
    if (process.server) {
      // er wordt een belofte geretourneerd
      return new Promise(function(resolve, reject) {
        // normaal gesproken is hier een asynchrone functie – hier dus niet
        // dit resultaat wordt opgenomen in de eigenschappen van [data]
        resolve({ value: 87 })
      })
    }
  },
  • uit regel 3 bleek dat de functie [asyncData] door de client werd uitgevoerd nog vóór de functies van de levenscyclus;
  • regel 5 heeft de uitvoering van de rest van de code die de eigenschap [value] aanmaakte, verhinderd, omdat deze code door de client wordt uitgevoerd;

Laten we nu verdergaan met de pagina [page3]:

Image

  • in [2] was de waarde 28 toen de pagina door de server werd geleverd. Hier is dat niet het geval;
  • in [4] zien we dat de functie [fetch] aan de clientzijde is uitgevoerd;

Laten we de door de client uitgevoerde code eens bekijken:


...
fetch(context) {
    // wie voert deze code uit?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // alleen voor de server
    if (process.server) {
      // er wordt een belofte geretourneerd
      return new Promise(function(resolve, reject) {
        // normaal gesproken hebben we hier een asynchrone functie
        // we simuleren deze met een wachttijd van één seconde
        setTimeout(() => {
          // geslaagd
          resolve()
        }, 1000)
      }).then(() => {
        // de store wordt gewijzigd
        context.store.commit('increment', 28)
        // log
        console.log('fetch commit terminé')
      })
    }
},
...
  • de client voert de methode [fetch] uit, regel 2;
  • de regels 6-21 worden niet uitgevoerd omdat de voorwaarde [process.server] onwaar is. Regel 17, die de teller op 28 zet, wordt dus niet uitgevoerd. De teller blijft op nul staan. Daarom geeft de client 0 weer in plaats van 28;

Laten we nu naar de pagina [page4] gaan. We krijgen het volgende resultaat:

Image

  • in [2], de tellerstand;
  • in [3], de logbestanden van de client;

De door de client uitgevoerde code is als volgt:


...
data() {
    return {
      value: 0
    }
  },
  // levenscyclus
  async beforeCreate() {
    // client en server
    console.log('[page4 beforeCreate]')
    // alleen voor de server
    if (process.server) {
      // de asynchrone functie wordt uitgevoerd
      const valeur = await new Promise(function(resolve, reject) {
        // hier hebben we normaal gesproken een asynchrone functie
        // we simuleren deze met een wachttijd van 10 seconden
        setTimeout(() => {
          // geslaagd – de waarde van de teller wordt teruggegeven
          resolve(52)
        }, 10000)
      })
      // de store wordt gewijzigd
      this.$store.commit('increment', valeur)
      // log
      console.log('[page4 beforeCreate], fonction asynchrone terminée, compteur=', this.$store.state.counter)
    }
  },
  created() {
    // client en server
    this.value = this.$store.state.counter
    console.log('[page4 created], value=', this.value)
  },
...
  • de regels 12-26 worden niet door de client uitgevoerd, omdat de voorwaarde [process.server] in regel 12 onwaar is. De teller in de store [Vuex] is dus 0 (de beginwaarde in de store) en deze waarde wordt op de pagina weergegeven;

Je kunt je afvragen wat er gebeurt als je de voorwaarde [if] in de regels 12 en 26 uitcommentarieert. Hier is het antwoord:

  • de client voert deze keer de regels 14-25 uit, maar [nuxt] wacht niet op het einde van de asynchrone functie (zoals bij de server) en laat de teller dus op 0 staan;
  • na 10 seconden wordt de asynchrone functie beëindigd en wordt de teller in regel 23 op 52 gezet;
  • wanneer men opnieuw naar de pagina [page4] navigeert, wordt de waarde 52 weergegeven;

5.7. Résumé

Uit onze verschillende tests kunnen we de volgende punten onthouden:

  • als de pagina [index] externe gegevens moet bevatten, kunnen deze door de server worden opgehaald met de functie [asyncData];
  • als de server bij het laden van de pagina [index] een store [Vuex] moet initialiseren met externe gegevens, dan doet hij dat in de functie [fetch];
  • de door de server gegenereerde pagina en de door de client gegenereerde pagina moeten identiek zijn om het ‘sprongeffect’ te voorkomen dat ontstaat doordat de pagina van de client visueel de door de server verzonden en aanvankelijk weergegeven pagina vervangt;

We gaan andere aspecten van [nuxt] ontdekken aan de hand van een nieuw voorbeeld.