Skip to content

8. Beispiel [nuxt-05]: Persistenz des Stores mit einem Session-Cookie

Ziel: Der Speicher [Vuex] soll nicht bei jeder Anfrage an den Server zurückgesetzt werden. Dazu verwenden wir ein Session-Cookie:

  • Der Speicher wird vom Server initialisiert und von diesem in ein Session-Cookie geschrieben;
  • der Client-Browser erhält dieses Session-Cookie und sendet es automatisch bei jeder neuen Anfrage an den Server mit;
  • der Server kann dann dieses Session-Cookie abrufen und mit dem darin enthaltenen Speicher arbeiten, einem vom Client aktualisierten Speicher;

8.1. Présentation

Das Projekt [nuxt-05] wird zunächst durch Kopieren des Projekts [nuxt-04] erstellt:

Image

Wir werden sehen, dass sich nur die Datei [store / index.js] ändern wird.

Um Cookies mit [nuxt] zu verwenden, nutzen wir das Modul [cookie-universal-nuxt], das wir zusammen mit [yarn] in einem Terminal VSCode installieren:

Image

  • In [4] geben wir den Befehl [yarn add cookie-universal-nuxt] ein;

Dadurch wird der Datei [package.json] des Projekts [dvp] ein neues Modul hinzugefügt:


...
},
  "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. Die Konfigurationsdatei [nuxt.config.js]

Damit [nuxt] die Cookies von [cookie-universal-nuxt] nutzen kann, muss dieses Modul in der Konfigurationsdatei [nuxt.config.js] deklariert werden:


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

  • In Zeile 12 wird das Modul [cookie-universal-nuxt] zur Modultabelle [6] von [nuxt] hinzugefügt;

Die Datei [nuxt.config.js] sieht schließlich wie folgt aus:


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: [
    // Dok.: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Dok.: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Dok.: 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) {}
  },
  // Quellcode-Verzeichnis
  srcDir: 'nuxt-05',
  // Router
  router: {
    // Stammverzeichnis der Anwendung URL
    base: '/nuxt-05/'
  },
  // Server
  server: {
    // Dienstport, standardmäßig 3000
    port: 81,
    // Abgehörte Netzwerkadressen, standardmäßig localhost: 127.0.0.1
    // 0.0.0.0 = alle Netzwerkadressen des Rechners
    host: 'localhost'
  },
  // Umgebung
  env: {
    maxAge: 60 * 5
  }
}
  • Zeile 79: Der Schlüssel [env] wurde der Datei hinzugefügt. Dieser Schlüssel ist ein reserviertes Wort. Die in diesem Objekt deklarierten Elemente sind über das Objekt [context.env] in den Elementen der Anwendung verfügbar;
  • Zeile 80: Das Attribut [maxAge] gibt die maximale Lebensdauer des Sitzungs-Cookies an, gemessen ab dem Zeitpunkt der letzten Initialisierung des Cookies. Diese Dauer wird in Sekunden angegeben. Hier wurde eine Lebensdauer von 5 Minuten festgelegt;

8.3. Das Prinzip der Persistenz des Stores

Die zwischen Client und Server ausgetauschten Cookies sind auf beiden Seiten (Client und Server) verfügbar in:

  • [context.app.$cookies] dort, wo das Objekt [context] verfügbar ist, d. h. so gut wie überall;
  • [this.$cookies] innerhalb einer Ansicht;

Ein bestimmtes Cookie erhält man mit dem Ausdruck [...$cookies.get(‘nom_du_cookie’)]. Den Wert eines Cookies legt man mit dem Ausdruck [...$cookies.set(‘nom_du_cookie’, valeur_du_cookie)] fest.

Das Prinzip des persistenten Store-Cookies ist wie folgt:

  • Wenn der Server den Speicher in der Funktion [nuxtServerInit] initialisiert, wird der Zustand des Speichers in einem Cookie namens „session“ gespeichert;
  • Das Cookie „session“ ist dann Teil der Antwort HTTP des Servers. Es ist bekannt, dass ein Browser die Cookies, die der Server ihm gesendet hat, an den Server zurücksendet. Dies geschieht bei jeder neuen Anfrage, die er an den Server stellt. Es ist ebenfalls bekannt, dass der Server den Speicher innerhalb der Seite sendet, die er an den Client übermittelt;
  • Im Browser ruft die Client-Anwendung den vom Server gesendeten Speicher ab und führt anschließend ihre Aufgaben aus. Wir stellen sicher, dass bei jeder Änderung des Speichers dessen neuer Zustand in dem vom Browser gespeicherten Cookie „session“ abgelegt wird;
  • Wenn der Benutzer einen Aufruf an den Server erzwingt, sendet der Client-Browser automatisch alle Cookies zurück, die der Server ihm zuvor gesendet hat, insbesondere das Cookie mit dem Namen „session“;
  • Wenn der Server nach diesem Aufruf den Speicher erneut zurücksetzt, ruft er das Cookie namens „session“ ab und initialisiert den Zustand des Speichers mit dessen Wert;
  • Es besteht somit Kontinuität des Speichers zwischen Client und Server;

8.4. Initialisierung des Speichers

Der Speicher ist in der Datei [store / index.js] implementiert:


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

export const mutations = {
  // Zähler um einen Wert erhöhen [inc]
  increment(state, inc) {
    state.counter += inc
  },
  // Ersetzen des Zustands
  replace(state, newState) {
    for (const attr in newState) {
      state[attr] = newState[attr]
    }
  }
}

export const actions = {
  async nuxtServerInit(store, context) {
    // Wer führt diesen Code aus?
    console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
    // Wir warten auf das Ende eines Promises
    await new Promise(function(resolve, reject) {
      // Normalerweise gibt es hier eine asynchrone Funktion
      // Wir simulieren sie mit einer Wartezeit von einer Sekunde
      setTimeout(() => {
        // Sitzung initialisieren
        initStore(store, context)
        // Erfolg
        resolve()
      }, 1000)
    })
  }
}

function initStore(store, context) {
  // Gibt es ein Sitzungs-Cookie in der aktuellen Anfrage?
  const cookies = context.app.$cookies
  const session = cookies.get('session')
  if (!session) {
    // keine bestehende Sitzung
    console.log("nuxtServerInit, initialisation d'une nouvelle session")
    // Der Speicher wird initialisiert
    store.commit('increment', 77)
  } else {
    console.log("nuxtServerInit, reprise d'une session existante")
    // Der Speicher wird mit dem Session-Cookie aktualisiert
    store.commit('replace', session.store)
  }
  // Der Speicher wird in das Session-Cookie geschrieben
  cookies.set('session', { store: store.state }, { path: context.base, maxAge: context.env.maxAge })
  // Protokoll
  console.log('initStore terminé, store=', store.state)
}

Kommentare

  • Zeilen 2–5: Der Speicher besteht aus einem Zähler;
  • Zeilen 9–11: Dieser Zähler kann inkrementiert werden;
  • Zeilen 13–17: Der Status des Speichers kann auf einen neuen Status initialisiert werden. Diese Funktion dient dazu, eine mögliche Initialisierung des Speichers zu veranschaulichen, wenn dieser nicht wie hier ausschließlich auf den Zähler beschränkt ist;
  • Zeilen 21–35: Die Funktion [nuxtServerInit] hat sich nicht geändert;
  • Zeile 30: Wenn die Wartezeit von einer Sekunde abgelaufen ist, wird der Speicher mithilfe der Funktion in den Zeilen 38–56 initialisiert;
  • Zeilen 40–41: Zunächst wird das Cookie mit dem Namen „session“ abgerufen:
    1. Bei der ersten Ausführung der Anwendung und bei der ersten Anfrage an den Server existiert dieses Cookie noch nicht. Es wird dann erstellt (Zeile 53) und an den Client-Browser gesendet;
    2. Bei derselben Ausführung der Anwendung und bei den Anfragen Nr. 2, 3, … an den Server existiert dieses Cookie bereits, da der Client-Browser es mit jeder neuen Anfrage an den Server zurücksendet;
    3. Bei einem zweiten Ausführen der Anwendung und bei der ersten Anfrage an den Server kann dieses Cookie ebenfalls vorhanden sein. Denn am Ende von Schritt 1 wurde das Cookie mit einer bestimmten Lebensdauer im Browser gespeichert. Wenn diese Lebensdauer noch nicht abgelaufen ist, wird das Cookie mit dem Namen „session“ mit der ersten Anfrage an den Server gesendet

Zusammenfassend lässt sich sagen: Bei jeder Anfrage an den Server gilt: Ist das „session“-Cookie bereits im Browser des Clients gespeichert, erhält der Server es; andernfalls erhält er es nicht.

  • Zeilen 42–47: Wenn der Server das Session-Cookie nicht erhält, wird der Speicher in Zeile 46 initialisiert;
    • anschließend wird in Zeile 53 ein Cookie namens „session“ erstellt und in die Antwort HTTP des Servers eingefügt. Der Wert des Cookies ist das Objekt [{ store: store.state }]. Es ist also der Zustand des Speichers und nicht der Speicher selbst, der in das Session-Cookie geschrieben wird;
    • Der dritte Parameter der Funktion [set] ist ein Optionsobjekt:
      • [path] gibt an, an welches URL dieses Cookie zurückgesendet werden soll. [context.base] ist das Basis-URL der Anwendung [nuxt-05]. Diese ist in der Datei [nuxt.config.js] definiert:

  // Router
  router: {
    // Stammverzeichnis der URL-Dateien der Anwendung
    base: '/nuxt-05/'
},
      • [maxAge] ist die Lebensdauer des Cookies im Browser in Sekunden. Nach Ablauf dieser Zeit sendet der Browser das Cookie nicht mehr an den Server zurück. [context.env.maxAge] gibt wiederum einen Wert zurück, der in der Datei [nuxt.config.js] hinterlegt ist:
1
2
3
4
// Umgebung
  env: {
    maxAge: 60 * 5
}

[env] ist ein reserviertes Schlüsselwort der Konfigurationsdatei. Hier wird die Lebensdauer auf 5 Minuten festgelegt. Diese Dauer wird ab dem Zeitpunkt gemessen, zu dem der Browser das Sitzungs-Cookie zuletzt erhalten hat. Nach Ablauf dieser Zeit wird das Cookie nicht mehr an den Server zurückgesendet, der dann eine neue Sitzung starten muss;

  • Zeilen 48–50: Wenn der Server das Sitzungs-Cookie empfängt, wird der Status des Stores mit dem Objekt [store] aus dem Sitzungs-Cookie initialisiert. Zur Erinnerung: Dieses Objekt enthält den gespeicherten Status des Stores;
    • Anschließend wird in Zeile 53 das Sitzungs-Cookie in die Antwort an den Client-Browser eingefügt:
      • Die Funktion [get] ruft das Sitzungs-Cookie aus der vom Server empfangenen Anfrage ab;
      • die Funktion [set] fügt das Sitzungs-Cookie in die Antwort ein, die der Server an den Client-Browser sendet;

8.5. Inkrementierung des Store-Zählers

Die Inkrementierung des Zählers auf der Seite [index.vue] erfolgt wie folgt:


  // Ereignisverwaltung
  methods: {
    incrementCounter() {
      console.log('incrementCounter')
       // Zähler um 1 erhöhen
      this.$store.commit('increment', 1)
       // Änderung des angezeigten Werts
      this.value = this.$store.state.counter
       // Speichern des Speichers im Sitzungs-Cookie
      this.$cookies.set('session', { store: this.$store.state }, { path: this.$nuxt.context.base, maxAge: this.$nuxt.context.env.maxAge })
    }
}

Auf der Client-Seite muss der Speicher bei jeder Änderung im Session-Cookie gespeichert werden. Denn der Benutzer kann jederzeit manuell eine URL anfordern, und man muss dann in der Lage sein, einen aktuellen Speicher an den Server zu senden. Aus diesem Grund wird in Zeile 10 nach der Inkrementierung des Speicherszählers dessen Status im Session-Cookie gespeichert:

  • Die Cookies sind in der Eigenschaft [this.$cookies] verfügbar;
  • der Status des Stores [this.$store.state] wird in dem Cookie gespeichert, das dem Schlüssel [store] zugeordnet ist;
  • Der Pfad des Cookies lautet [context.base]. In einer Ansicht ist der Kontext unter [this.$nuxt.context] verfügbar;
  • die Lebensdauer des Cookies ist [context.env.maxAge] und hier in der Eigenschaft [this.$nuxt.context.env.maxAge] verfügbar;

8.6. Ausführung des Beispiels [nuxt-05]

Wir starten die Anwendung [nuxt-05]:

Image

Die folgenden Screenshots stammen aus einem Chrome-Browser. Wir rufen die URL URL [http://localhost:81/nuxt-05/] auf. Vergessen Sie nicht das abschließende „/“ hinter „/nuxt-05“, da Sie sonst nicht die erwarteten Ergebnisse erhalten:

Image

  • Bei [4] haben wir den Anfangswert des Speichers (77) erhalten;

Sehen wir uns die Browser-Logs an (F12):

Image

  • in [5-6] die Server-Logs;
  • In [7] ist zu sehen, dass der Server eine neue Sitzung startet. Das bedeutet, dass er kein Sitzungs-Cookie erhalten hat;
  • in [8]: Initialisierung des Zählers mit dem Wert 77;
  • in [9] zeigen die Seite [index] auf dem Server (9) und die auf dem Client (10) tatsächlich denselben Zählerwert an;

Sehen wir uns nun die vom Browser empfangenen Cookies an:

Image

  • Wählen Sie bei [1] die Registerkarte [Application] und anschließend die Option [Cookies] [2]. Wählen Sie unter allen Cookies Ihres Browsers dasjenige der Domain [http://localhost:81] aus;
  • unter [4] das Cookie mit dem Namen „session“. Falls Sie dieses nicht haben, laden Sie die Seite [F5] neu: Möglicherweise ist die Lebensdauer von 5 Minuten bereits abgelaufen;
  • in [5] den Wert des Cookies. Obwohl dieser aufgrund der Zeichenkodierung { : nicht sehr gut lesbar ist, lässt sich der Wert 77 des Zählers erkennen;
  • in [6] die URL des Cookies: Jedes Mal, wenn diese URL angefordert wird, sendet der Browser das Cookie an den Server;
  • in „[7]“ ist die Ablaufzeit des Cookies angegeben. Sobald diese Zeit überschritten ist, wird das Cookie im Browser gelöscht;

Stellen Sie sicher, dass Sie dieses Cookie haben. Falls nicht, laden Sie die Seite neu (F5). Sobald die Seite mit dem Cookie angezeigt wird, laden Sie die Seite erneut (F5). Die Logs sehen dann wie folgt aus:

Image

Diesmal, bei [3], hat der Server das Sitzungs-Cookie korrekt abgerufen. Es wurde ihm vom Client-Browser gesendet.

Erhöhen Sie nun den Zählerwert und laden Sie die aktuelle Seite von Zeit zu Zeit neu (F5), sei es bei [index] oder bei [page1], Sie werden feststellen, dass der Zähler nicht wie im Beispiel [nuxt-04] auf 77 zurückspringt, sondern den Wert beibehält, den er im Client-Browser vor dem Neuladen der Seite hatte:

Image

Image

Die Browser-Protokolle sehen dann wie folgt aus:

Image

Hinweis: Für Testzwecke müssen Sie möglicherweise das im Browser gespeicherte Sitzungs-Cookie „[5]“ löschen, um bei der nächsten Anfrage an den Server mit einer neuen, vom Server initialisierten Sitzung zu beginnen.

Schauen wir uns abschließend den Einfluss der Funktion [incrementCounter] der Seite [index] auf das im Browser des Clients gespeicherte Sitzungs-Cookie an:


// Ereignisverwaltung
  methods: {
    incrementCounter() {
      console.log('incrementCounter')
      // Zähler um 1 erhöhen
      this.$store.commit('increment', 1)
      // Änderung des angezeigten Werts
      this.value = this.$store.state.counter
      // Speichern des Speichers im Session-Cookie
      this.$cookies.set('session', { store: this.$store.state }, { path: this.$nuxt.context.base, maxAge: this.$nuxt.context.env.maxAge })
    }
  }
  • Zeile 10: Die Änderung des Zählers wird auf das Sitzungs-Cookie übertragen;

Lassen Sie uns dies überprüfen. Wir gehen von folgender Situation aus:

Image

  • in [4] spiegelt der Zähler des Session-Cookies den angezeigten Wert [1] korrekt wider;

Erhöhen wir nun den Zähler einmal auf [5]. Das Session-Cookie [4] verändert sich wie folgt:

Image

  • zu [7] – der Zähler des Sitzungs-Cookies ist tatsächlich auf 84 gestiegen. Um dies zu sehen, muss die Ansicht [8] aktualisiert werden. Wählen Sie dazu eine andere Option von [Storage] (z. B. [9]) aus und wählen Sie anschließend erneut die Option [8] aus. Der neue Wert des Session-Cookies sollte dann angezeigt werden;