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:

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:

- 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’:
- 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;
- 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;
- 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]:
[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;
- vervolgens wordt in regel 53 het sessiecookie opgenomen in het antwoord aan de clientbrowser:
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]:

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:

- in [4] hebben we de beginwaarde van de store (77) verkregen;
Laten we de browserlogs bekijken (F12):

- 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:

- 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:

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:


De browserlogs zien er dan als volgt uit:

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:

- 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:

- 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;