Zusätzliche SSR-Leistung mit Nuxt Fullstack-Server (Teil 2)

Zusätzliche SSR-Leistung mit Nuxt Fullstack-Server


In Teil 1 habe ich darüber gesprochen, wie einfach es ist, einen API-Server in Nuxt zu organisieren . In Teil 2 möchte ich Ihnen erklären, welche zusätzlichen Vorteile Sie vom Nuxt-Fullstack-Server erhalten können .

Teil 2: Beschleunigung des Server-Renderings!


Lassen Sie uns nun anhand des Beispiels Codesandbox.io/s/codesandbox-nuxt-3gzhl überlegen, wie unser Server funktioniert

  1. Der Client fordert die Hauptseite 3gzhl.sse.codesandbox.io an
  2. Nuxt beginnt auf dem Server mit dem Rendern der Seite /pages/index.vue
  3. Kommt zu

      async fetch() {
        this.users = await this.$api("users", "index");
      },
    
  4. Über axios stellt er eine http-Anfrage unter 3gzhl.sse.codesandbox.io/api/users/index, d.h. auf mich
  5. Eine Verbindung wird hergestellt, eine neue Sitzung auf dem Server erstellt und Speicher für die Verarbeitung der http-Anforderung zugewiesen
  6. Eine eingehende Anfrage wird über das http- Protokoll akzeptiert , die URL wird analysiert, die Parameter werden verarbeitet
  7. Führt Server-Middleware aus
  8. Nuxt startet unseren API-Server
  9. JSON
  10. users.index(), JSON
  11. JSON http
  12. axios JSON
  13. API

Stellen Sie sich nun vor, wir haben 20 Komponenten auf der Seite, die Daten über die API anfordern. In einer Anforderung für die Seite mit dem Nuxt- Server werden 20 zusätzliche interne http- Verbindungen hergestellt und die Schritte 4 bis 13 werden 20 Mal ausgeführt. Der Nuxt-HTTP-Server kann mehr als 55.000 Anforderungen pro Sekunde verarbeiten. Wenn Sie jedoch interne HTTP-Anforderungen erstellen, reduzieren wir die potenziellen Serverressourcen um das Zehnfache.

Wenn wir die Seite auf dem Server rendern, haben wir direkten Zugriff auf alle Controller im Ordner / api /

Ändern wir die Logik so, dass beim Rendern auf dem Server der Controller-Code direkt aufgerufen wird und beim Aufruf vom Browser die Anforderung über http gesendet wurde

  1. Benennen Sie die Datei /plugins/api-context.js in /plugins/api-context.client.js um
  2. Ändern Sie den Dateinamen in den Einstellungen /nuxt.config.js

      plugins: ["~/plugins/api-context.client.js"]
    

    Jetzt ist dieser. $ Api- Kontext nur für Client-Code verfügbar
  3. Erstellen Sie diesen. $ api- Kontext , um die Controller direkt auf dem Server

    /plugins/api-context.server.js aufzurufen
    export default (context, inject) => {
      inject("api", async (controller, method, params) => {
        try {
          let api = require("../api/" + controller.replace(/^\/+|\/+$|\.+/g, ""));
          return await api[method](params);
        } catch (e) {
          console.error(e);
          throw e;
        }
      });
    };
    
  4. Verbinden Sie das Server-Plugin

    /nuxt.config.js
      plugins: [
        "~/plugins/api-context.client.js",
        "~/plugins/api-context.server.js"
      ]
    

Jetzt ruft die Funktion this. $ Api auf dem Server die Controller-Methode direkt auf, und auf dem Client dies. $ Api sendet eine http-Anfrage über axios .

Der Code

  async fetch() {
    this.users = await this.$api("users", "index");
  },

Beim Rendern auf dem Server wird keine http-Anforderung für sich selbst ausgeführt, sondern einfach die Datei /api/users.js über require verbunden und die index () -Methode aufgerufen , d. h. Elemente von 4 bis 13 werden nicht ausgeführt, aber nur 10 werden ausgeführt. Wenn der Client jedoch im Browser auf die Schaltfläche Aktualisieren klickt , werden dieselben Daten über http angefordert . Hier ist der vollständige Code: Codesandbox.io/s/codesandbox-nuxt-pbriw





Leistungstest


Codesandbox.io/s/codesandbox-nuxt-rzdyw

  1. Um den Einfluss der Geschwindigkeit externer Verbindungen zu beseitigen, habe ich den Empfang von Daten durch statische Daten ersetzt:

    /api/users.js
    // we can get data from any DB
    async function getDataFromDB() {
      return {
        page: 1,
        per_page: 6,
        total: 12,
        total_pages: 2,
        data: [
          {
            id: 1,
            email: "george.bluth@reqres.in",
            first_name: "George",
            last_name: "Bluth",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
          },
          {
            id: 2,
            email: "janet.weaver@reqres.in",
            first_name: "Janet",
            last_name: "Weaver",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
          },
          {
            id: 3,
            email: "emma.wong@reqres.in",
            first_name: "Emma",
            last_name: "Wong",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"
          },
          {
            id: 4,
            email: "eve.holt@reqres.in",
            first_name: "Eve",
            last_name: "Holt",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/marcoramires/128.jpg"
          },
          {
            id: 5,
            email: "charles.morris@reqres.in",
            first_name: "Charles",
            last_name: "Morris",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/stephenmoon/128.jpg"
          },
          {
            id: 6,
            email: "tracey.ramos@reqres.in",
            first_name: "Tracey",
            last_name: "Ramos",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/bigmancho/128.jpg"
          }
        ],
        ad: {
          company: "StatusCode Weekly",
          url: "http://statuscode.org/",
          text:
            "A weekly newsletter focusing on software development, infrastructure, the server, performance, and the stack end of things."
        }
      };
      /*
      return (await require("axios").get(`https://reqres.in/api/users?page=1`))
        .data;
      */
    }
    ....
    
  2. api , http

    /plugins/api-context.server.js
    export default (context, inject) => {
      inject("server", () => true);
      inject("api", async (controller, method, params) => {
        try {
          if (params && params.httpcall) {
            return await context.$axios["$" + (params ? "post" : "get")](
              "/api/" + controller + "/" + method,
              params
            );
          }
          let api = require("../api/" + controller.replace(/^\/+|\/+$|\.+/g, ""));
          return await api[method](params);
        } catch (e) {
          console.error(e);
          throw e;
        }
      });
    };
    
  3. index.vue fetch api 50

    /pages/index.vue
      async fetch() {
        let start = new Date();
        let promises = [];
        let callNum = 50;
        for (let i = 0; i < callNum; i++) {
          promises.push(this.$api("users", "index"));
        }
    
        let arr = await Promise.all(
          promises.map(async p => {
            return await p;
          })
        );
    
        let res = [];
        for (let r of arr) {
          res = res.concat(r);
        }
    
        this.users = res;
        this.fetchType =
          (this.$server && this.$server() ? "Server internal" : "Client http") +
          " API call";
        this.fetchTime = new Date() - start;
      },
    
  4. httpcall.vue fetch api 50 http

    /pages/httpcall.vue
    ...
          promises.push(this.$api("users", "index", { httpcall: true }));
    ...
        this.fetchType =
          (this.$server && this.$server() ? "Server http" : "Client http") +
          " API call";
    ...
    
  5. Vergleichen Sie nun die Ausführungszeit von rzdyw.sse.codesandbox.io

    Server internen API - Aufruf Rendering Abrufzeit: 1 ms
    Zeit von 0 ms bis maximal 2 ms

    rzdyw.sse.codesandbox.io/httpcall
    Server http - API - Aufruf Rendering holt Zeit: 71MS
    Zeit von 46ms bis maximal 1059ms
    und mehrmals stürzte der Server im Allgemeinen mit einem Fehler ab

    RangeError
    Maximum call stack size exceeded

Hier ist ein vollständiges Beispiel - Codesandbox.io/s/codesandbox-nuxt-rzdyw

Gesamtteil 2


  • Mit minimalen Änderungen können Sie das Rendern von Servern um mehr als das 50-fache beschleunigen. In einem Live-Beispiel wurde das Rendern meiner Seite um das ~ 1,7-fache beschleunigt
  • Die HTTP-Serverressourcen des Knotens wurden erheblich reduziert
  • In optimierter Weise sollte die einzige Instanz von Nuxt der Last kleiner und mittlerer Projekte standhalten

All Articles