Skip to content

17. Beispiel [nuxt-20]: Portierung des Beispiels [vuejs-22]

17.1. Présentation

Wir beabsichtigen hier, das Beispiel [vuejs-22], bei dem es sich um eine Anwendung vom Typ [vue.js] handelte, in einen Kontext vom Typ [nuxt] SSR zu portieren. [vuejs-22] war eine Client-Anwendung des Steuerberechnungsservers, die folgende Ansichten enthielt:

Die erste Ansicht ist die Authentifizierungsansicht:

Image

Die zweite Ansicht ist die Steuerberechnungsansicht:

Image

Die dritte Ansicht zeigt die Liste der vom Benutzer durchgeführten Simulationen an:

Image

Der obige Bildschirm zeigt, dass die Simulation Nr. 1 gelöscht werden kann. Daraufhin erhält man folgende Ansicht:

Image

Löscht man nun die letzte Simulation, erhält man die folgende neue Ansicht:

Image

Wir werden die Anwendung [vuejs-22] schrittweise auf die Anwendung [nuxt-20] umstellen. Wir werden den Code von [vuejs-22] nicht erneut erläutern. Der Leser wird gebeten, das Dokument |Einführung in das Framework VUE.JS anhand eines Beispiels| noch einmal durchzulesen. Die verschiedenen Schritte sollten die Unterschiede zwischen einer Anwendung [vuejs] und einer Anwendung [nuxt] verdeutlichen.

17.2. Schritt 1

Das Projekt [nuxt-20] wird zunächst durch Kopieren des Projekts [nuxt-12] erstellt. Dieses ist in der Tat ein guter Ausgangspunkt:

  • Es kann mit dem Steuerberechnungsserver kommunizieren;
  • es verarbeitet die vom Server gesendeten Fehler korrekt;
  • der Client und der Server [nuxt] können über eine Sitzung [nuxt] miteinander kommunizieren;

Wir verfügen also über eine gute Ausgangsbasis. Unsere Hauptaufgabe sollte darin bestehen, Folgendes anzupassen:

  • die Seiten. Wir werden die Seiten aus dem Projekt [vuejs-22] übernehmen und an die neue Umgebung anpassen;
  • die Verwaltung des Stores. Es sollten zusätzliche Informationen angezeigt werden (Liste der Simulationen), während andere möglicherweise überflüssig werden;
  • die Verwaltung des Client- und Server-Routings [nuxt];

Zunächst erstellen wir also das Projekt [nuxt-20], indem wir das Projekt [nuxt-12] kopieren:

Image

Anschließend werden die nicht mehr benötigten Seiten und Komponenten [2] gelöscht:

  • Die Komponente [components/navigation] verschwindet;
  • das Layout [layout/default] verschwindet;
  • die Seiten [index, authentification, get-admindata, fin-session] werden entfernt;

Anschließend werden Elemente aus [vuejs-22] und [3] in [nuxt-20] integriert:

  • Die drei Seiten [Authentification, CalculImpot, ListeSimulations] der Anwendung [vuejs-22] werden in den Ordner [pages] verschoben;
  • Die Komponenten [FormCalculImpot, Menu, Layout] der Anwendung [vuejs-22] werden in den Ordner [components] verschoben;
  • Die Seite [Main] aus [vuejs-22], die in der Anwendung [vuejs-22] als [layout] diente, wird in den Ordner [layouts] verschoben;

Die eingebetteten Elemente [4] werden umbenannt:

Image

  • In [layouts] wurde [Main] zu [default], da dies der Standardname für das Layout einer Anwendung [nuxt] ist;
  • in [pages] wurde die Seite [Authentification] zu [index], da [Authentification] diese Rolle in der Anwendung [vuejs-22] innehatte;

An dieser Stelle kann man das Projekt kompilieren, um erste Fehler zu erkennen. Man ändert die Datei [nuxt.config] aus dem Beispiel [nuxt-12], damit nun [nuxt-20] ausgeführt wird:


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

  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [
    { src: '@/plugins/client/plgSession', mode: 'client' },
    { src: '@/plugins/server/plgSession', mode: 'server' },
    { src: '@/plugins/client/plgDao', mode: 'client' },
    { src: '@/plugins/server/plgDao', mode: 'server' },
    { src: '@/plugins/client/plgEventBus', mode: 'client' }
  ],
  /*
   ** 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-20',
  // Router
  router: {
    // Stammverzeichnis der Anwendung URL
    base: '/nuxt-20/',
    // Routing-Middleware
    middleware: ['routing']
  },
  // 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: {
    // Axios-Konfiguration
    timeout: 2000,
    withCredentials: true,
    baseURL: 'http://„localhost/php7/scripts-web/impots/version-14“,
    // Konfiguration des Sitzungs-Cookies [nuxt]
    maxAge: 60 * 5
  }
}

Anschließend wird eine [build]-Datei des Projekts erstellt:

Image

Es werden folgende Fehler gemeldet:

1
2
3
4
5
6
7
Module not found: Error: Can't resolve '../assets/logo.jpg' @ ./nuxt-20/layouts/default.vue?...
Module not found: Error: Can't resolve './FormCalculImpot' @ ./nuxt-20/pages/calcul-impot.vue?...
Module not found: Error: Can't resolve './Layout' @ ./nuxt-20/pages/_.vue?...
Module not found: Error: Can't resolve './Layout' @ ./nuxt-20/pages/liste-des-simulations...
Module not found: Error: Can't resolve './Layout' @ ./nuxt-20/pages/calcul-impot.vue?...
Module not found: Error: Can't resolve './Menu' @ ./nuxt-20/pages/_.vue?...
Module not found: Error: Can't resolve './Menu' @ ./nuxt-20/pages/calcul-impot.vue
  • Der Fehler in Zeile 1 weist darauf hin, dass auf ein nicht vorhandenes Bild verwiesen wird. Dieses wird in [vuejs-22] abgerufen;
  • Der Fehler in Zeile 2 zeigt an, dass die Komponente [./FormCalculImpot] nicht existiert. Tatsächlich befindet sich diese Komponente nun in [@/components/form-calcul-impot];
  • Die Fehler in den Zeilen zu [3-5] zeigen, dass die Komponente [./Layout] nicht existiert. Tatsächlich befindet sich diese Komponente nun in [@/components/layout];
  • Die Fehler in den Zeilen [6-7] zeigen, dass die Komponente [./Menu] nicht existiert. Tatsächlich heißt sie nun [@/components/menu];

Das Bild [assets/logo.jpg] wird dem Projekt [nuxt-20] hinzugefügt:

Image

Außerdem werden wir auf allen Seiten den Pfad der Komponenten korrigieren. Nehmen wir als Beispiel die Seite [calcul-impot]:


<!-- Definition der Ansicht HTML -->
<template>
  <div>
    <Layout :left="true" :right="true">
      <!-- Formular zur Steuerberechnung auf der rechten Seite -->
      <FormCalculImpot slot="right" @resultatObtenu="handleResultatObtenu" />
      <!-- Navigationsmenü links -->
      <Menu slot="left" :options="options" />
    </Layout>
    <!-- Anzeigebereich für die Ergebnisse der Steuerberechnung unterhalb des Formulars -->
    <b-row v-if="résultatObtenu" class="mt-3">
      <!-- leerer Bereich mit drei Spalten -->
      <b-col sm="3" />
      <!-- neunsäuliger Bereich -->
      <b-col sm="9">
        <b-alert show variant="success">
          <span v-html="résultat"></span>
        </b-alert>
      </b-col>
    </b-row>
  </div>
</template>

<script>
// Importe
import FormCalculImpot from './FormCalculImpot'
import Menu from './Menu'
import Layout from './Layout'

export default {
  // verwendete Komponenten
  components: {
    Layout,
    FormCalculImpot,
    Menu
  },
  

Die drei [import] in den Zeilen 26–28 werden zu:


// Importe
import FormCalculImpot from '@/components/form-calcul-impot'
import Menu from '@/components/menu'
import Layout from '@/components/layout'

Auf diese Weise überprüft und korrigiert man gegebenenfalls die [import] aller Komponenten, Layouts und Seiten. Sobald diese Korrekturen vorgenommen wurden, kann man einen neuen [build] versuchen. Normalerweise treten dann keine Fehler mehr auf.

Man kann dann einen Durchlauf versuchen:

Image

Es treten Fehler auf:

1
2
3
4
5
6
Module Error (from ./node_modules/eslint-loader/dist/cjs.js):

c:\Data\st-2019\dev\nuxtjs\dvp\nuxt-20\pages\index.vue
   92:29  error  Expected '!==' and instead saw '!='  eqeqeq
  129:27  error  Expected '!==' and instead saw '!='  eqeqeq
150:28  error  Expected '!==' and instead saw '!='  eqeqeq

Man sieht, dass der Befehl [dev] in Verbindung mit dem Modul [eslint] syntaktisch strenger ist als der Befehl [build]. Hier wird verlangt, dass der Vergleichsoperator [!=] als [!==] geschrieben wird, da dieser Operator strenger ist (er überprüft zusätzlich den Typ der Operanden). Diese Fehler treten auf der Seite [index.vue] auf.

Wir beheben die oben genannten Fehler und starten die Ausführung des Projekts erneut. Daraufhin erhalten wir eine Warnung vom Modul [eslint]:

c:\Data\st-2019\dev\nuxtjs\dvp\nuxt-20\components\menu.vue
14:5  warning  Prop 'options' requires default value to be set  vue/require-default-prop

Image

Dieser Fehler wird mit dem Modul [Quick fix] aus den Modulen [eslint] und [2] behoben.

Das Projekt wird erneut ausgeführt. Es treten keine Kompilierungsfehler mehr auf. Anschließend wird die Datei URL [http://localhost:81/nuxt-20/] mit einem Browser aufgerufen. Dabei tritt ein Ausführungsfehler auf:

Image

Der Fehler befindet sich in [index.vue] [2]. Der Fehler [1] rührt daher, dass in [vuejs-22] die Ebene [dao] in [this.$dao] verfügbar war, während sie in [nuxt-12], dessen Infrastruktur wir übernommen haben, in der Funktion [this.$dao()] verfügbar ist.

Der Fehler befindet sich in der Funktion [created] des Seitenlebenszyklus [index]:

Image

Vorerst begnügen wir uns damit, [created] in [created2] umzubenennen, damit die Lebenszyklusfunktion [created] nicht ausgeführt wird: [3].

Wir speichern die Änderung und laden die Seite [index] erneut im Browser. Diesmal klappt es:

Image

17.3. Schritt 2

Die Seiten des Projekts [vuejs-22] verwendeten die folgenden eingefügten Elemente:

  • $dao: für die Ebene [dao] des Clients [vue.js];
  • $session: für eine im Browser unter [localStorage] gespeicherte Sitzung;

Diese Elemente existieren in der Infrastruktur des Projekts [nuxt-12], das wir kopiert haben, nicht mehr:

  • Es gibt nun zwei Ebenen [dao], eine für den Client [nuxt] und eine für den Server [nuxt]. Beide sind über eine eingebundene Funktion namens [$dao] verfügbar. Das bedeutet, dass in den Seiten der Anwendung [this.$dao] durch [this.$dao()] ersetzt werden muss;
  • die von der Anwendung [nuxt-20] verwaltete Sitzung [nuxt] hat nichts mehr mit dem Objekt [$session] derAnwendung [vuejs-22], in der es noch kein Konzept für Sitzungs-Cookies gab. Dennoch haben sie eine ähnliche Funktionalität: das Speichern persistenter Informationen im Verlauf der Benutzeraktionen. Die Sitzung [nuxt] speichert die Informationen im Speicher statt direkt in der Sitzung. Auf den Seiten der Anwendung muss [this.$session] durch [this.$store] ersetzt werden, wenn es darum geht, Informationen in der Sitzung zu speichern, und durch [this.$session()], wenn es darum geht, die Sitzung selbst zu bearbeiten;
  • um den Status einer Eigenschaft P des Stores abzufragen, muss [this.$store.state.P] geschrieben werden;
  • Um die Eigenschaft P des Stores zu ändern, muss [this.$store.commit(‘replace’, {P:value}] eingegeben werden;

Diese Änderungen nehmen wir auf der Seite [index] vor:


<!-- Definition der Ansicht HTML -->
<template>
  <Layout :left="false" :right="true">
    <template slot="right">
      <!-- Formular HTML – die Werte werden mit der Aktion [authentifier-utilisateur] übermittelt -->
      <b-form @submit.prevent="login">
        <!-- Titel -->
        <b-alert show variant="primary">
          <h4>Bienvenue. Veuillez vous authentifier pour vous connecter</h4>
        </b-alert>
        <!-- 1. Zeile -->
        <b-form-group label="Nom d'utilisateur" label-for="user" label-cols="3">
          <!-- Benutzereingabefeld -->
          <b-col cols="6">
            <b-form-input id="user" v-model="user" type="text" placeholder="Nom d'utilisateur" />
          </b-col>
        </b-form-group>
        <!-- 2. Zeile -->
        <b-form-group label="Mot de passe" label-for="password" label-cols="3">
          <!-- Eingabefeld „Passwort“ -->
          <b-col cols="6">
            <b-input id="password" v-model="password" type="password" placeholder="Mot de passe" />
          </b-col>
        </b-form-group>
        <!-- 3. Zeile -->
        <b-alert v-if="showError" show variant="danger" class="mt-3">L'erreur suivante s'est produite : {{ message }}</b-alert>
        <!-- Schaltfläche vom Typ [submit] in der dritten Zeile -->
        <b-row>
          <b-col cols="2">
            <b-button :disabled="!valid" variant="primary" type="submit">Valider</b-button>
          </b-col>
        </b-row>
      </b-form>
    </template>
  </Layout>
</template>

<!-- Dynamik der Ansicht -->
<script>
/* eslint-disable no-console */
import Layout from '@/components/layout'
export default {
  // verwendete Komponenten
  components: {
    Layout
  },
  // Komponentenstatus
  data() {
    return {
      // Benutzer
      user: '',
      // sein Passwort
      password: '',
      // Steuert die Anzeige einer Fehlermeldung
      showError: false,
      // die Fehlermeldung
      message: ''
    }
  },

  // berechnete Eigenschaften
  computed: {
    // Gültige Eingaben
    valid() {
      return this.user && this.password && this.$store.state.started
    }
  },
  // Lebenszyklus: Die Komponente wurde gerade erstellt
  mounted() {
    // eslint-disable-next-line
    console.log("Authentification mounted");
    // Kann der Benutzer Simulationen durchführen?
    if (this.$store.state.started && this.$store.state.authenticated && this.$métier.taxAdminData) {
      // Dann kann der Benutzer Simulationen durchführen
      this.$router.push({ name: 'calculImpot' })
      // Zurück zur Ereignisschleife
      return
    }
    // Wenn die Sitzung jSON bereits gestartet wurde, wird sie nicht erneut gestartet
    if (!this.$store.state.started) {
      // Wartephase beginnt
      this.$emit('loading', true)
      // Die Sitzung mit dem Server wird initialisiert – asynchrone Anfrage
      // Das von den Methoden der Schicht [dao] zurückgegebene Versprechen wird verwendet
      this.$dao()
        // Eine Sitzung wird initialisiert jSON
        .initSession()
        // Die Antwort wurde empfangen
        .then((response) => {
          // Wartezeit beendet
          this.$emit('loading', false)
          // Analyse der Antwort
          if (response.état !== 700) {
            // Der Fehler wird angezeigt
            this.message = response.réponse
            this.showError = true
            // Rückkehr zur Ereignisschleife
            return
          }
          // Die Sitzung wurde gestartet
          this.$store.commit('replace', { started: true })
          console.log('[authentification], session=', this.$session())
        })
        // im Fehlerfall
        .catch((error) => {
          // Der Fehler wird an die Ansicht [Main] weitergeleitet
          this.$emit('error', error)
        })
        // in jedem Fall
        .finally(() => {
          // wird die Sitzung gespeichert
          this.$session().save()
        })
    }
  },

  // Ereignisbehandler
  methods: {
    // ----------- Authentifizierung
    async login() {
      try {
        // Wartephase beginnt
        this.$emit('loading', true)
        // noch nicht authentifiziert
        this.$store.commit('replace', { authenticated: false })
        // blockierende Authentifizierung beim Server
        const response = await this.$dao().authentifierUtilisateur(this.user, this.password)
        // Ende des Ladevorgangs
        this.$emit('loading', false)
        // Auswertung der Serverantwort
        if (response.état !== 200) {
          // Der Fehler wird angezeigt
          this.message = response.réponse
          this.showError = true
          // Rückkehr zur Ereignisschleife
          return
        }
        // kein Fehler
        this.showError = false
        // Authentifizierung erfolgreich
        this.$store.commit('replace', { authenticated: true })
        // --------- Nun werden die Daten der Steuerbehörde angefordert
        // zunächst keine Daten
        this.$métier.setTaxAdminData(null)
        // Wartephase beginnt
        this.$emit('loading', true)
        // blockierende Anfrage an den Server
        const response2 = await this.$dao().getAdminData()
        // Ladevorgang abgeschlossen
        this.$emit('loading', false)
        // Antwort wird analysiert
        if (response2.état !== 1000) {
          // Fehler wird angezeigt
          this.message = response2.réponse
          this.showError = true
          // Rückkehr zur Ereignisschleife
          return
        }
        // kein Fehler
        this.showError = false
        // Die empfangenen Daten werden in der Schicht [métier] gespeichert
        this.$métier.setTaxAdminData(response2.réponse)
        // Es kann mit der Steuerberechnung fortgefahren werden
        this.$router.push({ name: 'calculImpot' })
      } catch (error) {
        // Der Fehler wird an die übergeordnete Komponente weitergeleitet
        this.$emit('error', error)
      } finally {
        // Speicher aktualisieren
        this.$store.commit('replace', { métier: this.$métier })
        // Die Sitzung wird gespeichert
        this.$session().save()
      }
    }
  }
}
</script>

Beachten Sie folgende Punkte:

  • Zeile 69: Die Funktion [created2] wurde in [mounted] umbenannt, damit der Server [nuxt] sie nicht ausführt (er führt weder [beforeMount] noch [mounted] aus). Nur der Client [nuxt] wird sie ausführen, wie es bereits beim Beispiel [vuejs-22] der Fall war;
  • Zeile 73: Es wird auf [this.$métier] verwiesen, das derzeit noch nicht existiert;
  • Zeile 75: Wir haben diese Methode in einer Anwendung [nuxt] noch nie verwendet. Es muss geprüft werden, ob sie in einem [nuxt]-Kontext funktioniert;
  • Zeile 112, 172: In [vuejs-22] wurde die Projektsitzung auf diese Weise gespeichert. Beim Projekt [nuxt-20] muss die Methode [save] den aktuellen Kontext erhalten. Es ist bekannt, dass auf einer Seite [nuxt] das Objekt [context] in [this.$nuxt.context] verfügbar ist;

Die Zeilen 112 und 172 werden daher wie folgt umgeschrieben:


this.$session().save(this.$nuxt.context)

Es ist zu beachten, dass dieser Code nicht optimiert ist. Anstatt die Funktion [this.$session()] mehrfach zu verwenden, wäre es besser, Folgendes zu schreiben:


const session=this.$session()

und anschließend die Variable [session] zu verwenden. Die gleiche Argumentation gilt für die Funktion [this.$dao()].

Nachdem diese Korrekturen vorgenommen wurden, können wir die Seiten URL und [http://localhost:81/nuxt-20/] erneut mit einem Browser aufrufen. Wir erhalten weiterhin dieselbe Seite wie zuvor:

Image

Sehen wir uns die Browser-Protokolle an:

Image

Das Protokoll [1] ist das letzte Protokoll, das vom Client [nuxt] erstellt wurde. In [2] sehen wir, dass die Eigenschaft [started] auf [vrai] gesetzt ist, was bedeutet, dass die Funktion [mounted] erfolgreich eine Sitzung jSON mit dem Steuerberechnungsserver gestartet hat. Außerdem ist zu erkennen, dass der Store Eigenschaften enthält, die entweder verworfen oder umbenannt werden müssen. Zur Erinnerung: Wir verwenden den Store aus dem Beispiel [nuxt-12].

Nun rufen wir die URL- und die [http://localhost:81/nuxt-20/]-Sitzung erneut auf, während der Steuerberechnungsserver nicht gestartet ist. Zunächst löschen wir das Cookie der Sitzung [nuxt]:

Image

Der obige Screenshot stammt aus Chrome. Anschließend liefert die Eingabe von URL und [http://localhost:81/nuxt-20/] das folgende Ergebnis:

Image

Der Fehler wurde vom Projekt [vuejs-22] korrekt behandelt. Er wird auch vom Projekt [nuxt-20] weiterhin korrekt behandelt.

17.4. Schritt 3

Nachdem wir nun die Anmeldeseite haben, müssen wir uns den Code ansehen, der ausgeführt wird, wenn der Benutzer auf die Schaltfläche [Valider] klickt:


// Ereignisbehandler
  methods: {
    // ----------- Authentifizierung
    async login() {
      try {
        // Wartephase beginnt
        this.$emit('loading', true)
        // noch nicht authentifiziert
        this.$store.commit('replace', { authenticated: false })
        // blockierende Authentifizierung beim Server
        const response = await this.$dao().authentifierUtilisateur(this.user, this.password)
        // Ende des Ladevorgangs
        this.$emit('loading', false)
        // Auswertung der Serverantwort
        if (response.état !== 200) {
          // Der Fehler wird angezeigt
          this.message = response.réponse
          this.showError = true
          // Rückkehr zur Ereignisschleife
          return
        }
        // kein Fehler
        this.showError = false
        // Authentifizierung erfolgreich
        this.$store.commit('replace', { authenticated: true })
        // --------- Nun werden die Daten der Steuerbehörde angefordert
        // zunächst keine Daten
        this.$métier.setTaxAdminData(null)
        // Wartephase beginnt
        this.$emit('loading', true)
        // blockierende Anfrage an den Server
        const response2 = await this.$dao().getAdminData()
        // Ladevorgang abgeschlossen
        this.$emit('loading', false)
        // Analyse der Antwort
        if (response2.état !== 1000) {
          // Es wird folgende Fehlermeldung angezeigt
          this.message = response2.réponse
          this.showError = true
          // Zurück zur Ereignisschleife
          return
        }
        // kein Fehler
        this.showError = false
        // Die empfangenen Daten werden in der Schicht [métier] gespeichert
        this.$métier.setTaxAdminData(response2.réponse)
        // Es kann mit der Steuerberechnung fortgefahren werden
        this.$router.push({ name: 'calculImpot' })
      } catch (error) {
        // Der Fehler wird an die übergeordnete Komponente weitergeleitet
        this.$emit('error', error)
      } finally {
        // Speicher aktualisieren
        this.$store.commit('replace', { métier: this.$métier })
        // Die Sitzung wird gespeichert
        this.$session().save(this.$nuxt.context)
      }
    }
  }

Das Hauptproblem scheint hier das Fehlen der Daten [this.$métier] zu sein. Um dies zu beheben, werden wir:

  • die Klasse [Métier] aus dem Beispiel [vuejs-22] einbinden. Wir werden sie im Ordner [api] ablegen;
  • eine Funktion [$métier] in den Kontext des Clients [nuxt] einfügen, die den Zugriff auf diese Klasse ermöglicht;

Zunächst das Kopieren der Klasse [Métier] in den Ordner [api]:

Image

Sobald die Klasse [Métier] im Projekt vorhanden ist, erstellen wir ein neues Plugin für den Client [nuxt]. Dieses Plugin mit dem Namen [pluginMétier] fügt eine Funktion [$métier] ein, die Zugriff auf die Klasse [Métier] gewährt:


/* eslint-disable no-console */
// Es wird ein Zugriffspunkt auf die Schicht [métier] erstellt
import Métier from '@/api/client/Métier'
export default (context, inject) => {
  // Instanziierung der Schicht [métier]
  const métier = new Métier()
  // Einfügen einer Funktion [$métier] in den Kontext
  inject('métier', () => métier)
  // Protokoll
  console.log('[fonction client $métier créée]')
}

Nachdem dies geschehen ist, können wir die Seite [index] korrigieren:


// Lebenszyklus: Die Komponente wurde gerade erstellt
  mounted() {
    // eslint-disable-next-line
    console.log("Authentification mounted");
    // Kann der Benutzer Simulationen durchführen?
    if (this.$store.state.started && this.$store.state.authenticated && this.$métier().taxAdminData) {
      // Dann kann der Benutzer Simulationen durchführen
      this.$router.push({ name: 'calcul-impot' })
      // Zurück zur Ereignisschleife
      return
    }
    // Wenn die Sitzung jSON bereits gestartet wurde, wird sie nicht erneut gestartet
    ...
  },

  // Ereignisbehandler
  methods: {
    // ----------- Authentifizierung
    async login() {
      try {
        // Wartephase beginnt
        this.$emit('loading', true)
        // Es wurde noch keine Authentifizierung durchgeführt
        this.$store.commit('replace', { authenticated: false })
        // blockierende Authentifizierung beim Server
        const response = await this.$dao().authentifierUtilisateur(this.user, this.password)
        // Ende des Ladevorgangs
        this.$emit('loading', false)
        // Auswertung der Serverantwort
        if (response.état !== 200) {
          // Der Fehler wird angezeigt
          this.message = response.réponse
          this.showError = true
          // Rückkehr zur Ereignisschleife
          return
        }
        // kein Fehler
        this.showError = false
        // Authentifizierung erfolgreich
        this.$store.commit('replace', { authenticated: true })
        // --------- Nun werden die Daten der Steuerbehörde angefordert
        // zunächst keine Daten
        this.$métier().setTaxAdminData(null)
        // Wartephase beginnt
        this.$emit('loading', true)
        // blockierende Anfrage an den Server
        const response2 = await this.$dao().getAdminData()
        // Ladevorgang abgeschlossen
        this.$emit('loading', false)
        // Antwort wird analysiert
        if (response2.état !== 1000) {
          // Fehler wird angezeigt
          this.message = response2.réponse
          this.showError = true
          // Rückkehr zur Ereignisschleife
          return
        }
        // kein Fehler
        this.showError = false
        // Die empfangenen Daten werden in der Schicht [métier] gespeichert
        this.$métier().setTaxAdminData(response2.réponse)
        // Es kann mit der Steuerberechnung fortgefahren werden
        this.$router.push({ name: 'calcul-impot' })
      } catch (error) {
        // Der Fehler wird an die übergeordnete Komponente weitergeleitet
        this.$emit('error', error)
      } finally {
        // Speicher aktualisieren
        this.$store.commit('replace', { métier: this.$métier() })
        // Die Sitzung wird gespeichert
        this.$session().save(this.$nuxt.context)
      }
    }
  }
  • Zeilen 43, 61, 69: [this.$métier] wurde durch [this.$métier()] ersetzt;
  • Zeilen 8, 63: Der Name der Seite [CalculImpot] aus dem Projekt [vuejs-22] wurde in die Seite [calcul-impot] im Projekt [nuxt-20] geändert;

Nachdem diese Korrekturen vorgenommen wurden, kann versucht werden, die Anmeldeseite zu validieren:

Image

Die resultierende Seite sieht wie folgt aus:

Image

Wir haben die Seite zur Steuerberechnung erfolgreich aufgerufen. Sehen wir uns nun die Protokolle an:

Image

In [2] ist zu sehen, dass die Authentifizierung korrekt gespeichert wurde. In [3-4] ist zu sehen, dass die Daten aus [taxAdminData] abgerufen wurden, die die Steuerberechnung durch die Klasse [Métier] ermöglichen.

17.5. Schritt 4

Sehen wir uns die Seite [calcul-impot] an, die wir erhalten haben:


<!-- Definition HTML der Ansicht -->
<template>
  <div>
    <Layout :left="true" :right="true">
      <!-- Formular zur Steuerberechnung auf der rechten Seite -->
      <FormCalculImpot slot="right" @resultatObtenu="handleResultatObtenu" />
      <!-- Navigationsmenü links -->
      <Menu slot="left" :options="options" />
    </Layout>
    <!-- Anzeigebereich für die Ergebnisse der Steuerberechnung unterhalb des Formulars -->
    <b-row v-if="résultatObtenu" class="mt-3">
      <!-- leerer Bereich mit drei Spalten -->
      <b-col sm="3" />
      <!-- neunsäuliger Bereich -->
      <b-col sm="9">
        <b-alert show variant="success">
          <span v-html="résultat"></span>
        </b-alert>
      </b-col>
    </b-row>
  </div>
</template>

<script>
// Importe
import FormCalculImpot from '@/components/form-calcul-impot'
import Menu from '@/components/menu'
import Layout from '@/components/layout'

export default {
  // verwendete Komponenten
  components: {
    Layout,
    FormCalculImpot,
    Menu
  },
  // interner Bericht
  data() {
    return {
      // Menüoptionen
      options: [
        {
          text: 'Liste des simulations',
          path: '/liste-des-simulations'
        },
        {
          text: 'Fin de session',
          path: '/fin-session'
        }
      ],
      // Ergebnis der Steuerberechnung
      résultat: '',
      résultatObtenu: false
    }
  },
  // Lebenszyklus
  created() {
    // eslint-disable-next-line
    console.log("CalculImpot created");
  },
  // Methoden zur Ereignisverwaltung
  methods: {
    // Ergebnis der Steuerberechnung
    handleResultatObtenu(résultat) {
      // Das Ergebnis wird als Zeichenkette aufgebaut HTML
      const impôt = "Montant de l'impôt : " + résultat.impôt + ' euro(s)'
      const décôte = 'Décôte : ' + résultat.décôte + ' euro(s)'
      const réduction = 'Réduction : ' + résultat.réduction + ' euro(s)'
      const surcôte = 'Surcôte : ' + résultat.surcôte + ' euro(s)'
      const taux = "Taux d'imposition : " + résultat.taux
      this.résultat = impôt + '<br/>' + décôte + '<br/>' + réduction + '<br/>' + surcôte + '<br/>' + taux
      // Anzeige des Ergebnisses
      this.résultatObtenu = true
      // ---- Aktualisierung des Stores [Vuex]
      // eine Simulation von +
      this.$store.commit('addSimulation', résultat)
      // Die Sitzung wird gespeichert
      this.$session.save()
    }
  }
}
</script>
  • Zeilen 44 und 48: Die Links im Navigationsmenü sind korrekt. Die Seite [/fin-session] existiert nicht. Das Projekt [vuejs-22] hat dieses Problem mithilfe von Weiterleitungen gelöst. Wir werden dasselbe mit dem Projekt [nuxt-20] tun;
  • Zeile 76: Es wird auf eine Mutation [addSimulation] verwiesen, die derzeit noch nicht existiert. Wir werden sie anlegen;
  • Zeile 78: Wie auf der Seite [index] muss hier [this.$session().save(this.$nuxt.context)] stehen;

Ändern wir den Store [store/index]. Er wurde vom Projekt [nuxt-12] übernommen und sieht derzeit wie folgt aus:


/* eslint-disable no-console */

// Status des Stores
export const state = () => ({
  // Sitzung jSON gestartet
  jsonSessionStarted: false,
  // Benutzer authentifiziert
  userAuthenticated: false,
  // Sitzungs-Cookie PHP
  phpSessionCookie: '',
  // adminData
  adminData: ''
})

// Änderungen im Store
export const mutations = {
  // Ersetzen des Status
  replace(state, newState) {
    for (const attr in newState) {
      state[attr] = newState[attr]
    }
  },
  // Speicher zurücksetzen
  reset() {
    this.commit('replace', { jsonSessionStarted: false, userAuthenticated: false, phpSessionCookie: '', adminData: '' })
  }
}

// Aktionen des Speichers
export const actions = {
  nuxtServerInit(store, context) {
    // Wer führt diesen Code aus?
    console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
    // Sitzung initialisieren
    initStore(store, context)
  }
}

function initStore(store, context) {
  // Der Speicher ist der zu initialisierende Speicher
  // Die Sitzung wird abgerufen
  const session = context.app.$session()
  // Wurde die Sitzung bereits initialisiert?
  if (!session.value.initStoreDone) {
    // Ein neuer Store wird gestartet
    console.log("nuxtServerInit, initialisation d'un nouveau store")
    // Der Store wird in die Sitzung aufgenommen
    session.value.store = store.state
    // Der Speicher ist nun initialisiert
    session.value.initStoreDone = true
  } else {
    console.log("nuxtServerInit, reprise d'un store existant")
    // Der Speicher wird mit dem Speicher der Sitzung aktualisiert
    store.commit('replace', session.value.store)
  }
  // Die Sitzung wird gespeichert
  session.save(context)
  // Protokoll
  console.log('initStore terminé, store=', store.state)
}
  • Zeilen 3–27: Wir übernehmen den Status und die Änderungen aus der Anwendung [vuejs-22] (siehe Dokument [3]):

// Jalousiestatus
export const state = () => ({
  // Sitzung jSON gestartet
  started: false,
  // Benutzer authentifiziert
  authenticated: false,
  // Sitzungs-Cookie PHP
  phpSessionCookie: '',
  // Liste der Simulationen
  simulations: [],
  // Nummer der letzten Simulation
  idSimulation: 0,
  // Ebene [métier]
  métier: null
})

// Änderungen an der Jalousie
export const mutations = {
  // Ersetzen des Zustands
  replace(state, newState) {
    for (const attr in newState) {
      state[attr] = newState[attr]
    }
  },
  // Zurücksetzen des Speichers
  reset() {
        this.commit('replace', { started: false, authenticated: false, phpSessionCookie: '', idSimulation: 0, simulations: [], métier: null })
  },
  // Löschen der Zeile mit Index-Nr.
  deleteSimulation(state, index) {
    // eslint-disable-next-line no-console
    console.log('mutation deleteSimulation')
    // Zeile Nr. [index] wird gelöscht
    state.simulations.splice(index, 1)
    console.log('store simulations', state.simulations)
  },
  // Hinzufügen einer Simulation
  addSimulation(state, simulation) {
    // eslint-disable-next-line no-console
    console.log('mutation addSimulation')
    // Nummer der Simulation
    state.idSimulation++
    simulation.id = state.idSimulation
    // Die Simulation wird zur Simulationsliste hinzugefügt
    state.simulations.push(simulation)
  }
}
  • Zeilen 4 und 6: Wir fügen die bereits verwendeten Eigenschaften ein;
  • Zeile 8: Wir behalten das Sitzungs-Cookie PHP bei. Es ist unerlässlich, damit der Client und der Server [nuxt] dieselbe Sitzung PHP mit dem Steuerberechnungsserver haben;
  • Zeile 10: Die Liste der vom Benutzer durchgeführten Simulationen;
  • Zeile 12: die Nummer der letzten vom Benutzer durchgeführten Simulation;
  • Zeile 14: die Ebene [métier];
  • Zeilen 30–47: die im Speicher des Projekts [vuejs-22] vorhandenen Mutationen, auf die von den Seiten der Anwendung verwiesen wird. Das Projekt [vuejs-22] hatte eine Mutation namens [clear], die die Liste der Simulationen leerte. Diese wird nicht aufgenommen, da die bereits vorhandene Mutation [reset] ausreichen sollte;
  • Zeilen 26–28: Die Mutation [reset] wird angepasst, um den neuen Inhalt des Zustands zu berücksichtigen;

Die Seite [calcul-impot] verwendet die folgende Komponente [form-calcul-impot]:


<!-- Definition HTML der Ansicht -->
<template>
  <!-- Formular HTML -->
  <b-form @submit.prevent="calculerImpot" class="mb-3">
    <!-- Meldung in 12 Spalten auf blauem Hintergrund -->
    <b-row>
      <b-col sm="12">
        <b-alert show variant="primary">
          <h4>Remplissez le formulaire ci-dessous puis validez-le</h4>
        </b-alert>
      </b-col>
    </b-row>
    <!-- Formularelemente -->
    <!-- erste Zeile -->
    <b-form-group label="Etes-vous marié(e) ou pacsé(e) ?">
      <!-- Optionsfelder in 5 Spalten-->
      <b-col sm="5">
        <b-form-radio v-model="marié" value="oui">Oui</b-form-radio>
        <b-form-radio v-model="marié" value="non">Non</b-form-radio>
      </b-col>
    </b-form-group>
    <!-- zweite Zeile -->
    <b-form-group label="Nombre d'enfants à charge" label-for="enfants">
      <b-form-input id="enfants" v-model="enfants" :state="enfantsValide" type="text" placeholder="Indiquez votre nombre d'enfants"></b-form-input>
      <!-- eventuelle Fehlermeldung -->
      <b-form-invalid-feedback :state="enfantsValide">Vous devez saisir un nombre positif ou nul</b-form-invalid-feedback>
    </b-form-group>
    <!-- dritte Zeile -->
    <b-form-group label="Salaire annuel net imposable" label-for="salaire" description="Arrondissez à l'euro inférieur">
      <b-form-input id="salaire" v-model="salaire" :state="salaireValide" type="text" placeholder="Salaire annuel"></b-form-input>
      <!-- mögliche Fehlermeldung -->
      <b-form-invalid-feedback :state="salaireValide">Vous devez saisir un nombre positif ou nul</b-form-invalid-feedback>
    </b-form-group>
    <!-- vierte Zeile, Schaltfläche [submit] -->
    <b-col sm="3">
      <b-button :disabled="formInvalide" type="submit" variant="primary">Valider</b-button>
    </b-col>
  </b-form>
</template>

<!-- Skript -->
<script>
export default {
  // interner Status
  data() {
    return {
      // verheiratet oder nicht
      marié: 'non',
      // Anzahl der Kinder
      enfants: '',
      // Jahresgehalt
      salaire: ''
    }
  },
  // berechneter interner Status
  computed: {
    // Formularfreigabe
    formInvalide() {
      return (
        // Gehalt ungültig
        !this.salaire.match(/^\s*\d+\s*$/) ||
        // oder ungültige Kinderangaben
        !this.enfants.match(/^\s*\d+\s*$/) ||
        // oder Steuerdaten nicht abgerufen
        !this.$métier.taxAdminData
      )
    },
    // Lohnvalidierung
    salaireValide() {
      // muss ein numerischer Wert >= 0 sein
      return Boolean(this.salaire.match(/^\s*\d+\s*$/) || this.salaire.match(/^\s*$/))
    },
    // Validierung der Kinder
    enfantsValide() {
      // muss ein numerischer Wert >= 0 sein
      return Boolean(this.enfants.match(/^\s*\d+\s*$/) || this.enfants.match(/^\s*$/))
    }
  },
  // Lebenszyklus
  created() {
    // Protokoll
    // eslint-disable-next-line
    console.log("FormCalculImpot created");
  },
  // Ereignis-Handler
  methods: {
    calculerImpot() {
      // Die Steuer wird mithilfe der Schicht [métier] berechnet
      const résultat = this.$métier.calculerImpot(this.marié, Number(this.enfants), Number(this.salaire))
      // eslint-disable-next-line
      console.log("résultat=", résultat);
      // Das Ergebnis wird vervollständigt
      résultat.marié = this.marié
      résultat.enfants = this.enfants
      résultat.salaire = this.salaire
      // das Ereignis [resultatObtenu] wird ausgelöst
      this.$emit('resultatObtenu', résultat)
    }
  }
}
</script>
  • Zeilen 65, 89: Die Referenz [this.$métier] muss in [this.$métier()] geändert werden;

Nachdem diese Korrekturen vorgenommen wurden, kann eine Simulation gestartet werden:

Image

Man erhält folgende Antwort:

Image

Wenn man sich die Protokolle ansieht:

Image

  • in [9-10] sieht man, dass sich die erste Simulation tatsächlich in [store] befindet;
  • Bei [5] wurde die Nummer der letzten Simulation tatsächlich erhöht;

17.6. Schritt 5

Nachdem wir nun eine Simulation durchgeführt haben, klicken wir auf den Link [Liste des simulations]. Wir gelangen auf die folgende Seite:

Image

Die Weiterleitung des Clients [nuxt] ist korrekt erfolgt. Sehen wir uns den Code der Seite [liste-des-simulations] an:


<!-- Definition HTML der Ansicht -->
<template>
  <div>
    <!-- Layout -->
    <Layout :left="true" :right="true">
      <!-- Simulationen in der rechten Spalte -->
      <template slot="right">
        <template v-if="simulations.length == 0">
          <!-- keine Simulationen -->
          <b-alert show variant="primary">
            <h4>Votre liste de simulations est vide</h4>
          </b-alert>
        </template>
        <template v-if="simulations.length != 0">
          <!-- Es gibt Simulationen -->
          <b-alert show variant="primary">
            <h4>Liste de vos simulations</h4>
          </b-alert>
          <!-- Tabelle der Simulationen -->
          <b-table :items="simulations" :fields="fields" striped hover responsive>
            <template v-slot:cell(action)="data">
              <b-button @click="supprimerSimulation(data.index)" variant="link">Supprimer</b-button>
            </template>
          </b-table>
        </template>
      </template>
      <!-- Navigationsmenü in der linken Spalte -->
      <Menu slot="left" :options="options" />
    </Layout>
  </div>
</template>

<script>
// Importe
import Layout from '@/components/layout'
import Menu from '@/components/menu'
export default {
  // Komponenten
  components: {
    Layout,
    Menu
  },
  // interner Status
  data() {
    return {
      // Optionen des Navigationsmenüs
      options: [
        {
          text: "Calcul de l'impôt",
          path: '/calcul-impot'
        },
        {
          text: 'Fin de session',
          path: '/fin-session'
        }
      ],
      // Tabellenparameter HTML
      fields: [
        { label: '#', Schlüssel: 'id' },
        { label: 'Marié', key: 'marié' },
        { label: "Nombre d'enfants", key: 'enfants' },
        { label: 'Salaire', key: 'salaire' },
        { label: 'Impôt', key: 'impôt' },
        { label: 'Décôte', key: 'décôte' },
        { label: 'Réduction', key: 'réduction' },
        { label: 'Surcôte', key: 'surcôte' },
        { label: '', key: 'action' }
      ]
    }
  },
  // berechneter interner Status
  computed: {
    // Liste der aus dem Vuex-Store abgerufenen Simulationen
    simulations() {
      return this.$store.state.simulations
    }
  },
  // Lebenszyklus
  created() {
    // eslint-disable-next-line
    console.log("ListeSimulations created");
  },
  // Methoden
  methods: {
    supprimerSimulation(index) {
      // eslint-disable-next-line
      console.log("supprimerSimulation", index);
      // Entfernung der Simulation Nr. [index]
      this.$store.commit('deleteSimulation', index)
      // Die Sitzung wird gespeichert
      this.$session.save()
    }
  }
}
</script>
  • Zeilen 47–56: Die Ziele des Navigationsmenüs sind korrekt;
  • Zeile 75: Der Store ist korrekt referenziert;
  • Zeile 89: Es wird eine Mutation [deleteSimulation] verwendet, die wir im vorherigen Schritt integriert haben;
  • Zeile 91: Diese Zeile muss in [this.$session().save(this.$nuxt.context)] umgeschrieben werden;

Wir nehmen die erforderlichen Änderungen vor und versuchen anschließend, die angezeigte Simulation zu löschen:

Image

Daraufhin wird folgende Seite angezeigt:

Image

Sehen wir uns die Protokolle an:

Image

  • In [6] sieht man, dass die Simulationstabelle leer ist;

Kehren wir nun zum Formular zur Steuerberechnung zurück:

Image

Man gelangt auf die folgende Seite:

Image

Die Weiterleitung hat also funktioniert.

17.7. Schritt 6

Nun müssen wir noch die Navigationsoption [Fin de session] im Navigationsmenü einrichten:


// Menüoptionen
      options: [
        {
          text: 'Liste des simulations',
          path: '/liste-des-simulations'
        },
        {
          text: 'Fin de session',
          path: '/fin-session'
        }
]
  • Zeile 9: Die Seite [/fin-session] existiert nicht. Das Projekt [vuejs-22] hat diesen Fall mit Routing-Regeln in einer Datei [router.js] behandelt:

// Importe
import Vue from 'vue'
import VueRouter from 'vue-router'
// die Ansichten
import Authentification from './views/Authentification'
import CalculImpot from './views/CalculImpot'
import ListeSimulations from './views/ListeSimulations'
import NotFound from './views/NotFound'
// die Sitzung
import session from './session'

// Routing-Plugin
Vue.use(VueRouter)

// Anwendungsrouten
const routes = [
  // Authentifizierung
  { path: '/', name: 'authentification', component: Authentification },
  { path: '/authentification', name: 'authentification2', component: Authentification },
  // Steuerberechnung
  {
    path: '/calcul-impot', name: 'calculImpot', component: CalculImpot,
    meta: { authenticated: true }
  },
  // Liste der Simulationen
  {
    path: '/liste-des-simulations', name: 'listeSimulations', component: ListeSimulations,
    meta: { authenticated: true }
  },
  // Sitzung beenden
  {
    path: '/fin-session', name: 'finSession'
  },
  // Unbekannte Seite
  {
    path: '*', name: 'notFound', component: NotFound,
  },
]

// der Router
const router = new VueRouter({
  // die Routen
  routes,
  // Anzeigemodus von URL
  mode: 'history',
  // die Basis der Anwendung URL
  base: '/client-vuejs-impot/'
})

// Routenprüfung
router.beforeEach((to, from, next) => {
  // eslint-disable-next-line no-console
  console.log("router to=", to, "from=", from);
  // Route nur für authentifizierte Benutzer?
  if (to.meta.authenticated && !session.authenticated) {
    next({
      // Weiter zur Authentifizierung
      name: 'authentification',
    })
    // Zurück zur Ereignisschleife
    return;
  }
  // Sonderfall: Ende der Sitzung
  if (to.name === "finSession") {
    // Die Sitzung wird bereinigt
    session.clear();
    // Wechsel zur Ansicht [authentification]
    next({
      name: 'authentification',
    })
    // Zurück zur Ereignisschleife
    return;
  }
  // andere Fälle – nächste normale Ansicht des Routings
  next();
})

// Export des Routers
export default router
  • Die Zeilen 64–76 behandelten den Sonderfall der Weiterleitung zum Pfad [/fin-session];
  • Zeile 66: Die aktuelle Sitzung wird geleert;
  • Zeilen 68–70: Die Ansicht [authentification] wird angezeigt;

Wir werden versuchen, etwas Ähnliches in der Routing-Datei des Clients [nuxt] zu implementieren:

Image

Das Skript [client/routing.js] sieht nun wie folgt aus:


/* eslint-disable no-console */
export default function(context) {
  // Wer führt diesen Code aus?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // Verwaltung des Sitzungs-Cookies PHP im Browser
  // Das Sitzungs-Cookie PHP des Browsers muss mit dem in der Nuxt-Sitzung gefundenen identisch sein
  // Die Aktion [fin-session] erhält ein neues Cookie PHP (sowohl vom Server als auch vom Nuxt-Client)
  // Wenn der Server es empfängt, muss der Client es an den Browser weiterleiten
  // für den eigenen Datenaustausch mit dem Server PHP
  // Hier handelt es sich um ein Client-Routing

  // Das Sitzungs-Cookie wird abgerufen: PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // Falls vorhanden, wird das Sitzungs-Cookie PHP dem Browser zugewiesen
    document.cookie = phpSessionCookie
  }

  // Wohin geht es weiter?
  const to = context.route.path
  if (to === '/fin-session') {
    // Die Sitzung wird bereinigt
    const session = context.app.$session()
    session.reset(context)
    // Weiterleitung zur Startseite
    context.redirect({ name: 'index' })
  }
}
  • Wir haben die Zeilen [19-27] zum bestehenden Code hinzugefügt;
  • Zeile 20: Man ruft [path] aus dem Ziel der aktuellen Route ab;
  • Zeile 21: Es wird geprüft, ob es sich um [/fin-session] handelt. Wenn ja:
    • Zeilen 23–24: Die Sitzung wird zurückgesetzt;
    • Zeile 26: Der Client [nuxt] wird auf die Startseite umgeleitet;

Die Methode [session.reset(context)] (Zeile 24) der Sitzung lautet wie folgt:


// Die Sitzung wird zurückgesetzt
  reset(context) {
    console.log('nuxt-session reset')
    // Speicher zurücksetzen
    context.store.commit('reset')
    // den neuen Speicher in der Sitzung speichern und die Sitzung speichern
    this.save(context)
}

Die Methode [context.store.commit('reset')] (Zeile 5) lautet wie folgt:


// Speicher zurücksetzen
  reset() {
        this.commit('replace', { started: false, authenticated: false, phpSessionCookie: '', idSimulation: 0, simulations: [], métier: null })
}

Wenn man nun den Link [Fin de session] verwendet, wird die Startseite mit den folgenden Protokollen angezeigt:

Image

  • In [3] sieht man, dass man nicht mehr authentifiziert ist;
  • in [4] sieht man, dass die Sitzung jSON gestartet wurde;
  • In [6] ist die Ebene [métier] nicht mehr im Speicher vorhanden (sie ist weiterhin auf den Seiten mit [this.$métier()] vorhanden);
  • in [5, 7] gibt es keine Simulationen mehr;

Man muss genau verstehen, was am Ende einer Sitzung geschieht:

  • Die Sitzung [nuxt] wird zurückgesetzt: Die Eigenschaft [started] des Stores wechselt zu [false];
  • es erfolgt eine Weiterleitung zur Seite [index];
  • die Methode [mounted] der Seite [index] wird ausgeführt. Diese startet eine neue Sitzung jSON mit dem Steuerberechnungsserver. Wenn der Vorgang erfolgreich ist, wechselt die Eigenschaft [started] des Stores zu [true];

17.8. Schritt 7

Zu diesem Zeitpunkt verfügt die Anwendung [nuxt-20] über alle Funktionen der Anwendung [vuejs-22]. Die Portierung scheint abgeschlossen zu sein.

Wir werden im Sinne von [nuxt] noch einen Schritt weitergehen. Die Methode [mounted] der Seite [index] stellt ein Problem dar. Sie startet einen asynchronen Vorgang, auf dessen Ende eine Suchmaschine nicht warten wird. Es ist bekannt, dass in diesem Fall der asynchrone Vorgang in eine Funktion [asyncData] ausgelagert werden muss, da der Server [nuxt], der ihn ausführt, dann wartet, bis er abgeschlossen ist, bevor er die Seite an die Suchmaschine übergibt.

Wir nutzen hier die Funktion [asyncData], die in der Anwendung [nuxt-12] für die Seite [index] geschrieben wurde:


export default {
  name: 'InitSession',
  // verwendete Komponenten
  components: {
    Layout,
    Navigation
  },
  // Asynchrone Daten
  async asyncData(context) {
    // Protokoll
    console.log('[index asyncData started]')
    // Man macht nichts zweimal, wenn die Seite bereits angefordert wurde
    if (process.server && context.store.state.jsonSessionStarted) {
      console.log('[index asyncData canceled]')
      return { result: '[succès]' }
    }
    try {
      // eine Sitzung wird gestartet jSON
      const dao = context.app.$dao()
      const response = await dao.initSession()
      // Protokoll
      console.log('[index asyncData response=]', response)
      // Das Sitzungs-Cookie PHP wird für die nächsten Anfragen abgerufen
      const phpSessionCookie = dao.getPhpSessionCookie()
      // Das Sitzungs-Cookie PHP wird in der Sitzung [nuxt] gespeichert
      context.store.commit('replace', { phpSessionCookie })
      // Ist ein Fehler aufgetreten?
      if (response.état !== 700) {
        // Der Fehler befindet sich in response.réponse
        throw new Error(response.réponse)
      }
      // Es ist zu vermerken, dass die Sitzung jSON gestartet wurde
      context.store.commit('replace', { jsonSessionStarted: true })
      // Das Ergebnis wird ausgegeben
      return { result: '[succès]' }
    } catch (e) {
      // Protokoll
      console.log('[index asyncData error=]', e)
      // Es wird vermerkt, dass die Sitzung jSON nicht gestartet wurde
      context.store.commit('replace', { jsonSessionStarted: false })
      // Der Fehler wird gemeldet
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // Der Speicher wird gesichert
      const session = context.app.$session()
      session.save(context)
      // Protokoll
      console.log('[index asyncData finished]')
    }
  },
  // Lebenszyklus
  beforeCreate() {
    console.log('[index beforeCreate]')
  },
  created() {
    console.log('[index created]')
  },
  beforeMount() {
    console.log('[index beforeMount]')
  },
  mounted() {
    console.log('[index mounted]')
    // nur Client
    if (this.showErrorLoading) {
      console.log('[index mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
}
  • Zeilen 13, 33, 40: Die Eigenschaft [jsonSessionStarted] muss in [started] geändert werden;
  • Zeile 13: In der Anwendung [nuxt-12] führte nur der Server [nuxt] die Seite [index] und deren Funktion [asyncData] aus. Der Client [nuxt] führte die Seite [index] erst aus, nachdem er sie vom Server [nuxt] erhalten hatte, und führte dabei die Funktion [asyncData] nicht aus. In [nuxt-20] ist dies anders: Der Link [Fin de session] zeigt die Seite [index] in der Umgebung des Clients [nuxt] an. Die Funktion [asyncData] wird dann ausgeführt. Wenn man jedoch auf diese Weise zur Seite [index] gelangt, wurde die Sitzung [nuxt] inzwischen zurückgesetzt, und die Eigenschaft [started] des Stores hat den Wert [false], sodass die Bedingung in Zeile 13 zwangsläufig falsch ist. Man kann also [process.server] beibehalten, sodass der Client [nuxt] diesen Test nicht durchführt;
  • Zeilen 15, 35, 42: Eine Eigenschaft [result] wird in die Eigenschaften [data] der Seite [index] eingefügt. In [nuxt-20] wird diese Eigenschaft nicht verwendet, daher entfernen wir sie aus dem von der Funktion zurückgegebenen Ergebnis;
  • Zeilen 61–67: Diese Methode [mounted] muss beibehalten werden, da sie es dem Client [nuxt] ermöglicht, die Fehlermeldung anzuzeigen. Die Art und Weise, wie der Fehler behandelt wird, wird jedoch geändert;

Auf der aktuellen Seite [index] integrieren wir die oben genannte Funktion [asyncData] anstelle der alten Funktion [mounted] und fügen eine neue Funktion [mounted] hinzu. Der Code der Seite [index] aus dem Beispiel [nuxt-20] sieht dann wie folgt aus:


...

<!-- Dynamik der Ansicht -->
<script>
/* eslint-disable no-console */
import Layout from '@/components/layout'
export default {
  // verwendete Komponenten
  components: {
    Layout
  },
  // Komponentenstatus
  data() {
    return {
      // Benutzer
      user: '',
      // sein Passwort
      password: '',
      // Fehleranzeige
      showError: false
    }
  },

  // berechnete Eigenschaften
  computed: {
    // Gültige Eingaben
    valid() {
      return this.user && this.password && this.$store.state.started
    }
  },
  // asynchrone Daten
  async asyncData(context) {
    // Protokoll
    console.log('[index asyncData started]')
    // Wird nicht doppelt ausgeführt, wenn die Seite bereits angefordert wurde
    if (process.server && context.store.state.started) {
      console.log('[index asyncData canceled]')
      return
    }
    try {
      // eine Sitzung wird gestartet jSON
      const dao = context.app.$dao()
      const response = await dao.initSession()
      // Protokoll
      console.log('[index asyncData response=]', response)
      // Das Sitzungs-Cookie PHP wird für die nächsten Anfragen abgerufen
      const phpSessionCookie = dao.getPhpSessionCookie()
      // Das Sitzungs-Cookie PHP wird in der Sitzung [nuxt] gespeichert
      context.store.commit('replace', { phpSessionCookie })
      // Ist ein Fehler aufgetreten?
      if (response.état !== 700) {
        // Der Fehler befindet sich in response.réponse
        throw new Error(response.réponse)
      }
      // Es ist zu beachten, dass die Sitzung jSON gestartet wurde
      context.store.commit('replace', { started: true })
      // kein Ergebnis
      return
    } catch (e) {
      // Protokoll
      console.log('[index asyncData error=]', e.message)
      // Es wird vermerkt, dass die Sitzung jSON nicht gestartet wurde
      context.store.commit('replace', { started: false })
      // Der Fehler wird gemeldet
      return { showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // Der Speicher wird gesichert
      const session = context.app.$session()
      session.save(context)
      // Protokoll
      console.log('[index asyncData finished]')
    }
  },
  // Lebenszyklus
  beforeCreate() {
    console.log('[index beforeCreate]')
  },
  created() {
    console.log('[index created]')
  },
  beforeMount() {
    // nur Client
    console.log('[index beforeMount]')
    // Behandlung eines möglichen Fehlers
    if (this.showErrorLoading) {
      // Protokoll
      console.log('[index beforeMount, showErrorLoading=true]')
      // Der Fehler wird an die Hauptkomponente weitergeleitet [default]
      this.$emit('error', new Error(this.errorLoadingMessage))
    }
  },
  mounted() {
    console.log('[index mounted]')
  },

  // Ereignisbehandler
  methods: {
    // ----------- Authentifizierung
    async login() {
      ...
}
</script>
  • Zeilen 58, 65: Die Funktion [asyncData] macht die Eigenschaft [result] hier nicht mehr ungenutzt;
  • Zeile 81: Die Methode [beforeMount] des Clients [nuxt]. Sie wurde der Methode [mounted] vorgezogen, um einen möglichen Fehler von [asyncData] zu behandeln;
  • Zeile 85: Es wird geprüft, ob die Eigenschaft [errorLoading] gesetzt wurde. Sie kann nur durch die Funktion [asyncData] gesetzt werden;
  • Zeilen 85–90: Wenn die Funktion [asyncData] einen Fehler gemeldet hat, wird sie über das Ereignis [error] an die Seite [default] weitergeleitet. So hat die alte Funktion [created], die wir gerade ersetzt haben, einen möglichen Fehler behandelt;

Führen wir einige Tests durch.

Zunächst löschen wir das Sitzungs-Cookie [nuxt] und das Sitzungs-Cookie PHP, sofern diese vorhanden sind. Anschließend rufen wir die Seite „[http://localhost:81/nuxt-20/]“ auf, während der Server zur Steuerberechnung nicht gestartet ist. Wir erhalten die folgende Seite:

Image

Wir laden dieselbe Seite erneut, nachdem wir den Steuerberechnungsserver gestartet haben:

Image

Sehen wir uns die Protokolle an:

Image

  • In [2-3] sehen wir, dass die Sitzung jSON gestartet wurde;
  • in [4] sieht man das Sitzungs-Cookie PHP, das der Server [nuxt] beim Austausch mit dem Steuerberechnungsserver abgerufen hat. Der Client [nuxt] wird es nun verwenden;

Nun wollen wir uns identifizieren:

Image

Wir erhalten die folgende Seite:

Image

Bei [1] haben wir eine Fehlermeldung erhalten. Das bedeutet, dass der Browser nicht das richtige Cookie der Sitzung PHP gesendet hat, die im vorherigen Schritt vom Server [nuxt] gestartet wurde. In [nuxt-12] erfolgte die Weitergabe des Sitzungs-Cookies PHP vom Server [nuxt] an den Client [nuxt] im Routing des Clients [nuxt] des Skripts [middleware/client/routing] :


/* eslint-disable no-console */
export default function(context) {  // Wer führt diesen Code aus?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // Verwaltung des Sitzungs-Cookies PHP im Browser
  // Das Sitzungs-Cookie PHP des Browsers muss mit dem in der Nuxt-Sitzung gefundenen identisch sein
  // Die Aktion [fin-session] erhält ein neues Cookie PHP (sowohl vom Server als auch vom Nuxt-Client)
  // Wenn der Server es empfängt, muss der Client es an den Browser weiterleiten
  // für seine eigenen Datenaustausche mit dem Server PHP
  // Hier handelt es sich um ein Client-Routing

  // Das Sitzungs-Cookie wird abgerufen: PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // Falls vorhanden, wird das Sitzungs-Cookie PHP dem Browser zugewiesen
    document.cookie = phpSessionCookie
  }

  // Wohin geht es weiter?
  const to = context.route.path
  if (to === '/fin-session') {
    // Die Sitzung wird bereinigt
    const session = context.app.$session()
    session.reset(context)
    // Weiterleitung zur Startseite
    context.redirect({ name: 'index' })
  }
}

Die Zeilen 13–17 ermöglichen es dem Client [nuxt], das Sitzungs-Cookie PHP vom Server [nuxt] abzurufen.

Das Problem hierbei ist, dass beim Klicken auf die Schaltfläche [Valider] keine Weiterleitung des Clients [nuxt] erfolgt. Seine Weiterleitungsfunktion wird daher nicht aufgerufen. Das Problem wird behoben, indem die Zeilen 12–17 am Anfang der Authentifizierungsmethode der Seite [index] dupliziert werden:


// Ereignisbehandler
  methods: {
    // ----------- Authentifizierung
    async login() {
      // Das Sitzungs-Cookie PHP wird aus dem Speicher abgerufen
      const phpSessionCookie = this.$store.state.phpSessionCookie
      if (phpSessionCookie) {
        // Falls vorhanden, wird das Sitzungs-Cookie PHP dem Browser zugewiesen
        document.cookie = phpSessionCookie
      }
      try {
        // Wartephase beginnt
        this.$emit('loading', true)
        // Man ist noch nicht authentifiziert

In den Zeilen 5–10 wird das Cookie der Sitzung PHP, die vom Server [nuxt] initiiert wurde, aus dem Speicher abgerufen. Nach dieser Änderung wird die Seite zur Steuerberechnung korrekt angezeigt, was bedeutet, dass die Authentifizierung funktioniert hat.

17.9. Schritt 8

Wir haben nun eine funktionsfähige Anwendung, die im Sinne von [nuxt] arbeitet. Wie bereits bei der Anwendung [nuxt-13] werden wir uns nun mit der Navigation des Servers [nuxt] befassen. Wie bereits erwähnt, soll der Benutzer die URL der Anwendung nicht manuell eingeben. Er soll die ihm angezeigten Links verwenden, die vom Client [nuxt] ausgeführt werden, der dann im Modus SPA läuft. Dennoch werden wir sicherstellen, dass die Navigation auf dem Server [nuxt] die Anwendung stets in einem stabilen Zustand belässt.

Aus der für [nuxt-13] durchgeführten Untersuchung (siehe Abschnitt „Link“) wissen wir, dass Folgendes erforderlich ist:

  • das Skript [midleware/routing] ändern;
  • ein Skript [middleware/server/routing] hinzufügen;

Image

Das Skript [middleware/routing] wird wie folgt geändert:


/* eslint-disable no-console */

// Die Server- und Client-Middleware wird importiert
import serverRouting from './server/routing'
import clientRouting from './client/routing'

export default function(context) {
  // Wer führt diesen Code aus?
  console.log('[middleware], process.server', process.server, ', process.client=', process.client)
  if (process.server) {
    // Server-Routing
    serverRouting(context)
  } else {
    // Client-Routing
    clientRouting(context)
  }
}
  • Zeile 4: Das Routing-Skript des Servers [nuxt] wird importiert;
  • Zeilen 10–12: Wenn der Server [nuxt] den Code ausführt, wird dessen Routing-Funktion verwendet;

Das Skript [middleware/server/routing] lautet wie folgt:


/* eslint-disable no-console */
export default function(context) {
  // Wer führt diesen Code aus?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)

  // Wir sammeln hier und da ein paar Informationen
  const store = context.store
  // Woher kommen wir?
  const from = store.state.from || 'nowhere'
  // Wohin gehen wir?
  let to = context.route.name
  
  // Sonderfall von /fin-session, der kein Attribut [name] hat
  if (context.route.path === '/fin-session') {
    to = 'fin-session'
  }
  
  // Mögliche Weiterleitung
  let redirection = ''
  // Routing-Verarbeitung abgeschlossen
  let done = false
  
  // Befindet man sich bereits in einer Weiterleitung des Servers [nuxt]?
  if (store.state.serverRedirection) {
    // keine Aktion erforderlich
    done = true
  }
  
  // Handelt es sich um ein Neuladen der Seite?
  if (!done && from === to) {
    // Nichts zu tun
    done = true
  }
  
  // Überprüfung der Servernavigation [nuxt]
  // Es wird das Navigationsmenü des Clients übernommen

  // zuerst wird der Fall „Sitzungsende“ behandelt
  if (!done && store.state.started && store.state.authenticated && to === 'fin-session') {
    // Die Sitzung wird bereinigt
    const session = context.app.$session()
    session.reset(context)
    // Weiterleitung zur Startseite
    redirection = 'index'
    // Auftrag abgeschlossen
    done = true
  }

  // Fall, in dem die Sitzung PHP nicht gestartet wurde
  if (!done && !store.state.started && to !== 'index') {
    // Weiterleitung zu [index]
    redirection = 'index'
    // Auftrag abgeschlossen
    done = true
  }

  // Fall, in dem der Benutzer nicht authentifiziert ist
  if (!done && store.state.started && !store.state.authenticated && to !== 'index') {
    redirection = 'index'
    // Auftrag abgeschlossen
    done = true
  }

  // Fall, in dem [adminData] nicht abgerufen wurde
  if (!done && store.state.started && store.state.authenticated && !store.state.métier.taxAdminData && to !== 'index') {
    // Weiterleitung zu [index]
    redirection = 'index'
    // Auftrag abgeschlossen
    done = true
  }

  // Falls [adminData] abgerufen wurde
  if (
    !done &&
    store.state.started &&
    store.state.authenticated &&
    store.state.métier.taxAdminData &&
    to !== 'calcul-impot' &&
    to !== 'liste-des-simulations'
  ) {
    // man bleibt auf derselben Seite
    redirection = from
    // Arbeit abgeschlossen
    done = true
  }

  // Normalerweise wurden alle Prüfungen durchgeführt ---------------------
  // Weiterleitung?
  if (redirection) {
    // Die Weiterleitung wird im Store vermerkt
    store.commit('replace', { serverRedirection: true })
  } else {
    // keine Weiterleitung
    store.commit('replace', { serverRedirection: false, from: to })
  }
  // Der Speicher wird in der Sitzung gespeichert [nuxt]
  const session = context.app.$session()
  session.value.store = store.state
  session.save(context)
  // Es erfolgt eine eventuelle Weiterleitung vom Server [nuxt]
  if (redirection) {
    context.redirect({ name: redirection })
  }
}
  • In diesem Skript greifen wir die Ideen auf, die bereits bei der Weiterleitung des Servers [nuxt] der Anwendung [nuxt-13] entwickelt und verwendet wurden;
  • Wir fügen dem Store der Anwendung zwei Eigenschaften hinzu:
    • [from]: der Name der zuletzt angezeigten Seite. Wir wissen, dass der Client [nuxt] über diese Information verfügt, der Server [nuxt] jedoch nicht. Wir werden diese Information hinzufügen, indem wir bei jeder Weiterleitung des Servers [nuxt] den Namen der Seite, die angezeigt werden soll, im Speicher ablegen. Das Gleiche tun wir bei jeder Weiterleitung des Clients [nuxt]. So findet der Server [nuxt] bei der nächsten Weiterleitung im Speicher den Namen der zuletzt von der Anwendung angezeigten Seite;
    • [serverRedirection]: Wenn ein Routing-Ziel vom Server [nuxt] abgelehnt wird, führt dieser eine Weiterleitung durch. Er vermerkt dann im Speicher, dass das nächste Ziel des Servers [nuxt] eine Weiterleitungsseite ist. Diese Weiterleitung löst eine erneute Ausführung des Routers des Servers [nuxt] aus. Stellt dieser fest, dass das aktuelle Ziel aus einer Weiterleitung stammt, lässt er den Vorgang zu;
  • Zeilen 6–11: Es werden die für das Routing relevanten Informationen abgerufen;
  • Zeilen 13–16: Das Ziel [/fin-session] ist keiner Seite zugeordnet, die [fin-session] heißen würde. Es hat also keinen Namen. Wir weisen ihm einen zu;
  • Zeile 19: das Ziel einer möglichen Weiterleitung;
  • Zeile 21: [done=true], sobald die Weiterleitungstests abgeschlossen sind;
  • Zeilen 23–27: Wie bereits erwähnt, ist nichts zu tun, wenn das aktuelle Routing aus einer Weiterleitung resultiert. Denn beim vorherigen Routing hat der Router entschieden, dass der Client-Browser weitergeleitet werden muss. Es besteht kein Grund, diese Entscheidung zu überdenken;
  • Zeilen 29–33: Handelt es sich um ein Neuladen der Seite, lässt man den Vorgang zu. Dies ist kein für alle Anwendungen [nuxt] geltendes Axiom: Die Auswirkungen eines Neuladens müssen für jede Seite einzeln geprüft werden. In diesem Fall hat das Neuladen der Seiten [index, calcul-impot, liste-des-simulations] keine unerwünschten Auswirkungen;
  • Zeilen 35–85: Die Routing-Konfiguration des Servers [nuxt] übernimmt die des Clients [nuxt]. Befindet man sich auf einer Seite, muss das Routing des Servers [nuxt] das Navigationsmenü widerspiegeln, das der Client [nuxt] auf dieser Seite anbietet;
  • Zeilen 38–47: Zunächst wird der Fall behandelt, dass das Ziel [fin-session] keiner vorhandenen Seite entspricht. Sind die Bedingungen erfüllt (Sitzung gestartet, Benutzer authentifiziert), wird die Sitzung zurückgesetzt und der Benutzer auf die Seite [index] weitergeleitet;
  • Zeilen 49–55: Wenn die Sitzung jSON mit dem Steuerberechnungsserver noch nicht gestartet wurde, ist die Seite [index] das einzig mögliche Ziel;
  • Zeilen 57–62: Wenn die Sitzung jSON gestartet wurde und der Benutzer nicht authentifiziert ist und die Authentifizierungsseite nicht angefordert hat, wird er auf die Authentifizierungsseite weitergeleitet, nämlich die Seite [index];
  • Zeilen 64–70: Wenn der Benutzer authentifiziert ist, die Daten [adminData] jedoch nicht abgerufen wurden, erfolgt eine Weiterleitung zur Authentifizierungsseite. Die Authentifizierung erfüllt zwei Aufgaben: Sie führt die Authentifizierung durch und fordert bei erfolgreicher Authentifizierung zusätzlich die Daten „[adminData]“ an. Wurden diese nicht abgerufen, muss die Authentifizierung wiederholt werden;
  • Zeilen 72–85: Wenn die Daten „[adminData]“ erhalten wurden, sind die einzigen möglichen Ziele „[calcul-impot]“ und „[liste-des-simulations]“. Ist dies nicht der Fall, wird die Weiterleitung abgelehnt;
  • Zeilen 88–95: Der Speicher wird je nachdem aktualisiert, ob eine Umleitung erfolgt oder nicht;
  • Zeile 94: Es findet keine Weiterleitung statt. Daher wird das aktuelle [to] zum [from] der nächsten Weiterleitung;
  • Zeilen 96–99: Die Informationen aus dem Store werden im Sitzungs-Cookie [nuxt] gespeichert;
  • Zeilen 100–103: Wenn eine Weiterleitung erforderlich ist, wird sie durchgeführt;

Um die Tests durchzuführen, muss darauf geachtet werden, dass man von einer leeren Situation ausgeht, indem man das Sitzungs-Cookie [nuxt] und das Sitzungs-Cookie PHP auf dem Steuerberechnungsserver löscht:

Image

Um das Routing des Servers [nuxt] zu testen, probieren Sie auf jeder Seite alle möglichen URL- und [/, /calcul-impot, /liste-des-simulations]-Werte aus. Die Anwendung muss dabei jedes Mal in einem konsistenten Zustand bleiben.

17.10. Schritt 9

In Schritt 9 erfolgt die Bereitstellung der Anwendung [nuxt-20]. Hierfür ist ein Hosting-Anbieter erforderlich, der eine [node.js]-Umgebung zur Ausführung des Servers [nuxt] bereitstellt. Ich verfüge nicht darüber. Der Leser kann die im Abschnitt „Link“ beschriebenen Schritte befolgen, um die Anwendung [nuxt-20] auf seinem Entwicklungsrechner bereitzustellen und sie mit einem Protokoll HTTPS zu sichern.

17.11. Conclusion

Die Portierung der Anwendung [vuejs-22] auf die Anwendung [nuxt-20] ist nun abgeschlossen. Hier einige wichtige Punkte zu dieser Portierung:

  • Die Seiten von [vuejs-22] wurden beibehalten;
  • die asynchronen Vorgänge, die in den Seiten von [vuejs-22] vorhanden waren, wurden in eine Funktion [asyncData] migriert;
  • in [nuxt-20] mussten zwei Entitäten verwaltet werden: der Client [nuxt] und der Server [nuxt]. Letztere Entität existierte in [vuejs-22] nicht. Um die Konsistenz zwischen den beiden Entitäten zu gewährleisten, benötigten wir eine Sitzung [nuxt];
  • wir mussten das Routing des Servers [nuxt] verwalten;

In der Praxis ist es zweifellos vorteilhafter, direkt mit einer Architektur [nuxt] zu beginnen, als eine Architektur [vue.js] zu erstellen, die anschließend in eine Umgebung [nuxt] übertragen wird.