Skip to content

8. Voorbeeld [nuxt-05]: persistentie van de store met een sessiecookie

Doel: we willen dat de store [Vuex] niet bij elk verzoek aan de server opnieuw wordt geïnitialiseerd. Hiervoor gebruiken we een sessiecookie:

  • de store wordt door de server geïnitialiseerd en door deze in een sessiecookie geplaatst;
  • de browser van de klant ontvangt deze sessiecookie en stuurt deze automatisch mee bij elk nieuw verzoek aan de server;
  • de server kan dit sessiecookie vervolgens ophalen en werken met de store die het bevat, een store die door de client is bijgewerkt;

8.1. Présentation

Het project [nuxt-05] wordt in eerste instantie verkregen door het project [nuxt-04] te kopiëren:

Image

We zullen zien dat alleen het bestand [store / index.js] zal veranderen.

Om cookies te gebruiken met [nuxt], gaan we de module [cookie-universal-nuxt] gebruiken, die we samen met [yarn] installeren in een terminal VSCode:

Image

  • in [4] voeren we het commando [yarn add cookie-universal-nuxt] in;

Er wordt zo een nieuwe module toegevoegd aan het bestand [package.json] van het project [dvp]:


...
},
  "dependencies": {
    "@nuxtjs/axios": "^5.3.6",
    "bootstrap": "^4.1.3",
    "bootstrap-vue": "^2.0.0",
    "cookie-universal-nuxt": "^2.0.19",
    "nuxt": "^2.0.0"
},

8.2. Het configuratiebestand [nuxt.config.js]

Om ervoor te zorgen dat [nuxt] de cookies van [cookie-universal-nuxt] kan gebruiken, moet deze module worden gedeclareerd in het configuratiebestand [nuxt.config.js]:


...
],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    // https://www.npmjs.com/package/cookie-universal-nuxt
    'cookie-universal-nuxt'
  ],
...

  • op regel 12 wordt de module [cookie-universal-nuxt] toegevoegd aan de tabel met modules [6] van [nuxt];

Het bestand [nuxt.config.js] ziet er uiteindelijk als volgt uit:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    title: 'Introduction à [nuxt.js]',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: 'ssr routing loading asyncdata middleware plugins store'
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },
  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },
  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    // https://www.npmjs.com/package/cookie-universal-nuxt
    'cookie-universal-nuxt'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  },
  // broncodemap
  srcDir: 'nuxt-05',
  // router
  router: {
    // hoofdmap van de applicatie URL
    base: '/nuxt-05/'
  },
  // 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'
  },
  // omgeving
  env: {
    maxAge: 60 * 5
  }
}
  • regel 79: de sleutel [env] is aan het bestand toegevoegd. Deze sleutel is een gereserveerd woord. De elementen die in dit object zijn gedeclareerd, zijn beschikbaar via het object [context.env] in de elementen van de applicatie;
  • regel 80: het attribuut [maxAge] is de maximale levensduur van de sessiecookie, gemeten vanaf het moment dat de cookie voor het laatst is geïnitialiseerd. Deze duur wordt uitgedrukt in seconden. Hier is een levensduur van 5 minuten ingesteld;

8.3. Het principe van de persistentie van de store

De cookies die tussen de client en de server worden uitgewisseld, zijn aan beide kanten (client en server) beschikbaar in:

  • [context.app.$cookies], waar het object [context] beschikbaar is, d.w.z. vrijwel overal;
  • [this.$cookies] binnen een weergave;

Een specifieke cookie wordt verkregen met de uitdrukking [...$cookies.get(‘nom_du_cookie’)]. De waarde van een cookie wordt ingesteld met de uitdrukking [...$cookies.set(‘nom_du_cookie’, valeur_du_cookie)].

Het principe van de persistente cookie van de store is als volgt:

  • wanneer de server de store initialiseert in de functie [nuxtServerInit], wordt de status van de store opgeslagen in een cookie met de naam ‘session’;
  • de cookie ‘session’ maakt dan deel uit van het antwoord HTTP van de server. We weten dat een browser de cookies die de server hem heeft gestuurd, terugstuurt naar de server. Dit gebeurt bij elk nieuw verzoek dat de browser aan de server doet. We weten ook dat de server de store meestuurt in de pagina die hij naar de client verstuurt;
  • binnen de browser haalt de clienttoepassing de door de server verzonden store op en voert vervolgens haar taken uit. We zorgen ervoor dat telkens wanneer de clienttoepassing de store wijzigt, de nieuwe status ervan wordt opgeslagen in het door de browser geregistreerde ‘session’-cookie;
  • als de gebruiker een verzoek aan de server forceert, stuurt de clientbrowser automatisch alle cookies terug die de server eerder naar hem heeft gestuurd, met name het ‘session’-cookie;
  • wanneer de server na deze aanroep de store opnieuw initialiseert, haalt hij het ‘session’-cookie op en initialiseert hij de status van de store met de waarde daarvan;
  • er is dus continuïteit van de store tussen de client en de server;

8.4. Initialisatie van de store

De store is geïmplementeerd in het bestand [store / index.js]:


/* eslint-disable no-console */
export const state = () => ({
  // teller
  counter: 0
})

export const mutations = {
  // de teller met één verhogen [inc]
  increment(state, inc) {
    state.counter += inc
  },
  // vervanging van de state
  replace(state, newState) {
    for (const attr in newState) {
      state[attr] = newState[attr]
    }
  }
}

export const actions = {
  async nuxtServerInit(store, context) {
    // wie voert deze code uit?
    console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
    // we wachten op het einde van een belofte
    await new Promise(function(resolve, reject) {
      // normaal gesproken hebben we hier een asynchrone functie
      // we simuleren deze met een wachttijd van één seconde
      setTimeout(() => {
        // sessie initialiseren
        initStore(store, context)
        // geslaagd
        resolve()
      }, 1000)
    })
  }
}

function initStore(store, context) {
  // is er een sessiecookie in het huidige verzoek
  const cookies = context.app.$cookies
  const session = cookies.get('session')
  if (!session) {
    // er is geen bestaande sessie
    console.log("nuxtServerInit, initialisation d'une nouvelle session")
    // de store wordt geïnitialiseerd
    store.commit('increment', 77)
  } else {
    console.log("nuxtServerInit, reprise d'une session existante")
    // de store wordt bijgewerkt met de sessiecookie
    store.commit('replace', session.store)
  }
  // de store wordt in de sessiecookie geplaatst
  cookies.set('session', { store: store.state }, { path: context.base, maxAge: context.env.maxAge })
  // log
  console.log('initStore terminé, store=', store.state)
}

Opmerkingen

  • regels 2-5: de store bestaat uit een teller;
  • regels 9-11: deze teller kan worden opgehoogd;
  • regels 13-17: de status van de store kan worden geïnitialiseerd vanuit een nieuwe status. Deze functie is bedoeld om een mogelijke initialisatie van de store te laten zien wanneer deze niet beperkt is tot alleen de teller, zoals hier het geval is;
  • regels 21-35: de functie [nuxtServerInit] is niet gewijzigd;
  • regel 30: wanneer de wachttijd van één seconde is verstreken, wordt de store geïnitialiseerd met behulp van de functie in de regels 38-56;
  • regels 40-41: we beginnen met het ophalen van de cookie met de naam ‘session’:
    1. bij de eerste uitvoering van de applicatie en bij het eerste verzoek aan de server zal dit cookie nog niet bestaan. Het wordt dan aangemaakt (regel 53) en naar de browser van de klant verzonden;
    2. tijdens dezelfde uitvoering van de applicatie en bij de verzoeken nr. 2, 3, ... die naar de server worden gestuurd, zal dit cookie wel bestaan, omdat de browser van de klant het bij elk nieuw verzoek aan de server terugstuurt;
    3. bij een tweede uitvoering van de applicatie en bij het eerste verzoek aan de server kan dit cookie ook al bestaan. Na afloop van stap 1 is het cookie namelijk in de browser opgeslagen met een bepaalde levensduur. Als deze levensduur nog niet is verstreken, wordt het cookie met de naam ‘session’ meegestuurd bij het eerste verzoek aan de server

Samenvattend geldt voor elk verzoek aan de server: als het ‘session’-cookie al in de browser van de client is opgeslagen, ontvangt de server het; anders ontvangt de server het niet.

  • regels 42-47: als de server het sessiecookie niet ontvangt, wordt de store geïnitialiseerd door regel 46;
    • vervolgens wordt in regel 53 een cookie met de naam ‘session’ aangemaakt en in het antwoord HTTP van de server geplaatst. De waarde van het cookie is het object [{ store: store.state }]. Het is dus de status van de store en niet de store zelf die in het sessiecookie wordt geplaatst;
    • de derde parameter van de functie [set] is een optieobject:
      • [path] geeft aan naar welke URL deze cookie moet worden teruggestuurd. [context.base] is de basis-URL van de applicatie [nuxt-05]. Deze wordt gedefinieerd in het bestand [nuxt.config.js]:

  // router
  router: {
    // root van de URL van de applicatie
    base: '/nuxt-05/'
},
      • [maxAge] is de levensduur in seconden van de cookie in de browser. Na afloop van deze periode stuurt de browser de cookie niet meer terug naar de server. [context.env.maxAge] geeft ook hier weer een waarde weer die is vastgelegd in het bestand [nuxt.config.js]:
1
2
3
4
// omgeving
  env: {
    maxAge: 60 * 5
}

[env] is een gereserveerd trefwoord in het configuratiebestand. Hier wordt de levensduur ingesteld op 5 minuten. Deze duur wordt gemeten vanaf het moment dat de browser het sessiecookie voor het laatst heeft ontvangen. Na afloop van deze periode wordt het cookie niet meer teruggestuurd naar de server, die dan een nieuwe sessie moet starten;

  • regels 48-50: als de server het sessiecookie ontvangt, wordt de status van de store geïnitialiseerd met het object [store] van het sessiecookie. Zoals bekend bevat dit object de opgeslagen status van de store;
    • vervolgens wordt in regel 53 het sessiecookie opgenomen in het antwoord aan de clientbrowser:
      • de functie [get] haalt de sessiecookie op uit het verzoek dat de server heeft ontvangen;
      • de functie [set] plaatst de sessiecookie in het antwoord dat de server aan de clientbrowser stuurt;

8.5. Verhoging van de teller van de store

Het verhogen van de teller op de pagina [index.vue] verloopt als volgt:


  // gebeurtenisbeheer
  methods: {
    incrementCounter() {
      console.log('incrementCounter')
       // teller met 1 verhogen
      this.$store.commit('increment', 1)
       // wijziging van de weergegeven waarde
      this.value = this.$store.state.counter
       // opslag van de waarde in de sessiecookie
      this.$cookies.set('session', { store: this.$store.state }, { path: this.$nuxt.context.base, maxAge: this.$nuxt.context.env.maxAge })
    }
}

Aan de clientzijde moet de store telkens wanneer deze wordt gewijzigd, worden opgeslagen in de sessiecookie. De gebruiker kan namelijk op elk moment handmatig een URL opvragen en dan moet men in staat zijn om een bijgewerkte store naar de server te sturen. Daarom wordt in regel 10, na het verhogen van de teller van de store, de status ervan opgeslagen in de sessiecookie:

  • de cookies zijn beschikbaar in de eigenschap [this.$cookies];
  • de status van de store [this.$store.state] wordt opgeslagen in het cookie dat gekoppeld is aan de sleutel [store];
  • het pad van de cookie is [context.base]. In een weergave is de context beschikbaar in [this.$nuxt.context];
  • de levensduur van de cookie is [context.env.maxAge], hier beschikbaar in de eigenschap [this.$nuxt.context.env.maxAge];

8.6. Uitvoering van het voorbeeld [nuxt-05]

We starten de applicatie [nuxt-05]:

Image

De volgende schermafbeeldingen zijn gemaakt in de Chrome-browser. We roepen de URL [http://localhost:81/nuxt-05/] op. Vergeet de laatste / achter /nuxt-05 niet, anders krijgt u niet de verwachte resultaten:

Image

  • in [4] hebben we de beginwaarde van de store (77) verkregen;

Laten we de browserlogs bekijken (F12):

Image

  • in [5-6], de serverlogs;
  • in [7] zien we dat de server een nieuwe sessie start. Dit betekent dat hij geen sessiecookie heeft ontvangen;
  • in [8] wordt de teller geïnitialiseerd met de waarde 77;
  • in [9] geven de pagina [index] van de server (9) en die van de client (10) inderdaad dezelfde tellerwaarde weer;

Laten we nu eens kijken naar de cookies die door de browser zijn ontvangen:

Image

  • in [1], selecteer het tabblad [Application] en vervolgens de optie [Cookies] [2]. Kies uit alle cookies in uw browser die van het domein [http://localhost:81];
  • in [4] de cookie met de naam ‘session’. Als u deze niet hebt, laad dan de pagina [F5] opnieuw: misschien is de geldigheidsduur van 5 minuten verstreken;
  • in [5], de waarde van de cookie. Hoewel deze niet erg leesbaar is vanwege de codering van de tekens { :, is de waarde 77 van de teller te onderscheiden;
  • in [6], de URL van de cookie: telkens wanneer deze URL wordt opgevraagd, stuurt de browser de cookie naar de server;
  • in [7] staat de vervaltijd van het cookie. Zodra deze tijd is verstreken, wordt het cookie in de browser verwijderd;

Zorg ervoor dat u dit cookie hebt. Als u het niet hebt, laad de pagina dan opnieuw (F5). Zodra u de pagina met het cookie hebt, laadt u de pagina opnieuw (F5). De logbestanden zien er dan als volgt uit:

Image

Deze keer, in [3], heeft de server de sessiecookie inderdaad opgehaald. Deze is door de clientbrowser naar de server verzonden.

Verhoog nu de teller en ververs af en toe de huidige pagina (F5), of het nu [index] of [page1] is, dan zul je zien dat de teller niet teruggaat naar 77 zoals in het voorbeeld [nuxt-04], maar de waarde behoudt die hij in de browser van de klant had vóór het vernieuwen van de pagina:

Image

Image

De browserlogs zien er dan als volgt uit:

Image

Opmerking: voor testdoeleinden kan het nodig zijn om [5], de sessiecookie die in de browser is opgeslagen, te verwijderen om bij het volgende verzoek aan de server met een nieuwe, door de server geïnitialiseerde sessie te beginnen.

Laten we tot slot eens kijken naar de invloed van de functie [incrementCounter] van de pagina [index] op de sessiecookie die in de browser van de klant is opgeslagen:


// gebeurtenisbeheer
  methods: {
    incrementCounter() {
      console.log('incrementCounter')
      // teller met 1 verhogen
      this.$store.commit('increment', 1)
      // wijziging van de weergegeven waarde
      this.value = this.$store.state.counter
      // de waarde opslaan in de sessiecookie
      this.$cookies.set('session', { store: this.$store.state }, { path: this.$nuxt.context.base, maxAge: this.$nuxt.context.env.maxAge })
    }
  }
  • regel 10: de wijziging van de teller wordt doorgevoerd in de sessiecookie;

Laten we dit eens controleren. We gaan uit van de volgende situatie:

Image

  • in [4] geeft de teller van de sessiecookie inderdaad de weergegeven waarde [1] weer;

Laten we de teller nu één keer verhogen naar [5]. De sessiecookie, die nu [4] is, verandert als volgt:

Image

  • naar [7]; de teller van het sessiecookie is inderdaad op 84 gekomen. Om dit te zien, moet u de weergave [8] vernieuwen. Selecteer hiervoor een andere optie van [Storage] (bijvoorbeeld [9]) en selecteer vervolgens opnieuw de optie [8]. De nieuwe waarde van de sessiecookie zou dan moeten verschijnen;