Wie optimiere ich die Größe einer Nuxt.js-basierten Anwendung?

Wie optimiere ich die Größe einer Nuxt.js-basierten Anwendung?

Tagtäglich setzen wir alles daran, die Größe clientseitiger Anwendungen zu reduzieren, um bei geringer Bandbreite diese nicht zu überlasten und das Nutzererlebnis insgesamt zu verbessern. Interessant ist, dass die durchschnittliche Größe der von Webseiten geladenen Ressourcen von Jahr zu Jahr zunimmt. Die Ressourcengröße für eine ordnungsgemäß optimierte Website sollte etwa 1-1,5 MB betragen, jedoch nicht größer als 3 MB sein.

How to reduce the size of an application based on Nuxt.js?

https://httparchive.org/reports/page-weight?start=earliest&end=latest#bytesTotal

Um unser Frontend zu solch großartigen Ergebnissen zu führen, benötigen wir Werkzeuge, die in der Regel in Form von Abhängigkeiten node_modules geliefert werden (wir sprechen hier von einer sehr großen Anzahl von Abhängigkeiten).

Bei der Installation eines Moduls kann es dazu kommen, dass zusätzlich bis zu 20 Module generiert werden (Paketabhängigkeiten). Das führt zu einem derart schnellen Wachstum eines Projektes, dass es nach der Installation der erforderlichen Tools (für Transpilierung, Kompilierung, Tests, Linting) zu fast 1 GB anschwillt. In der Regel wird bei 1 GB die Größe nicht hinterfragt, denn unsere lokalen Computer verfügen über reichlich Festplattenspeicher, und die gelegentliche Installation all dieser Abhängigkeiten stellt für uns kein Problem dar. Während der Computer eines einzelnen Entwicklers dazu in der Lage ist, trifft dies auf eine Continuous Integration nicht zu. Diese Prozesse installieren viel häufiger Abhängigkeiten und teilen zudem Ressourcen mit anderen Projekten, was sich auf den Festplattenspeicher, die Leistung von E/A-Prozessen oder den Zustand der Internetverbindung selbst auswirkt.

Daher sollten wir uns nicht nur um den Endbenutzer Gedanken machen, sondern auch um die Freigabeumgebungen selbst, denn solch große Anwendungsgrößen führen zu einem verlängerten Freigabeprozess und möglicherweise folglich zu Frustration der Entwickler, und ich weiß aus Erfahrung, dass nichts ärgerlicher ist als eine lange Verarbeitung bei der Freigabe kleiner Aktualisierungen.

Es gibt mehrere Möglichkeiten der Optimierung. Wir als Frontend-Entwickler können uns mit dem beschäftigen, was uns am meisten schmerzt:  mit der Anzahl der Abhängigkeiten. In diesem Beitrag erfahren Sie, wie Sie die Anzahl der Pakete node_modules auf ein Minimum reduzieren können, vielleicht sogar auf null?

Lassen Sie uns node_modules loswerden

Am einfachsten ist es, nach der Erstellung der Anwendung nur die Abhängigkeiten zu behalten, die für die Ausführung der Anwendung im Produktionsmodus erforderlich sind. Zu diesem Zweck unterscheiden wir bei den installierten Modulen zwischen Produktionsabhängigkeiten – („dependencies“) und Entwicklungsabhängigkeiten („devDependencies“).

Zur besseren Veranschaulichung der Probleme und Lösungen, die Sie hier kennenlernen werden, betrachten wir ein Nuxt.js-Projekt, das mithilfe von  https://github.com/nuxt/create-nuxt-app erstellt wurde – mit einem zusätzlichen Modul @nuxtjs/pwa.

Schauen wir uns an, wie die Datei package.json aussieht:

 "dependencies": {
    "@nuxtjs/pwa": "^3.3.4",
    "core-js": "^3.8.2",
    "nuxt": "^2.14.12"
  },
  "devDependencies": {
    "@nuxtjs/eslint-config": "^5.0.0",
    "@nuxtjs/eslint-module": "^3.0.2",
    "babel-eslint": "^10.1.0",
    "eslint": "^7.18.0",
    "eslint-plugin-nuxt": "^2.0.0",
    "eslint-plugin-vue": "^7.4.1"
  }

Sieht prima aus: 9 Pakete, von denen 3 für den Produktionsbetrieb erforderlich sind. Werfen wir nun einen Blick auf die Größen der Pakete node_modules:

➜ nuxt-standalone git:(master) ✗ ls node_modules | wc -l && du -sh node_modules
     809
186M node_modules

Wir können feststellen, dass bei der Installation eines einfachen Projekts nur 9 Abhängigkeiten benötigt werden, aber in Wirklichkeit erhalten wir weitere 800 Abhängigkeiten obendrauf, was uns etwa 200 MB kostet.  node_modules in Projekten, mit denen wir täglich arbeiten, basieren beispielweise auf dem Mechanismus von mono repositories + yarn workspaces, und die Größe all dieser node_modules-Dateien beträgt im Durchschnitt 800 MB.

Erster Schritt – ausschließlich auf die Produktion bezogene Abhängigkeiten

Wir wissen, dass wir nicht alle Pakete benötigen, um die Anwendung ordnungsgemäß auszuführen. Die „devDependencies“ werden nur benötigt, um das Projekt zu erstellen, also erstellen wir das Projekt und behalten nur die Produktionsabhängigkeiten bei.

Yarn build 
yarn install --production 

„Yarn install --production“ installiert nur Produktionspakete („dependencies“), auch wenn zuvor Entwicklungsabhängigkeiten („devDependencies“) installiert wurden.

Schauen wir uns an, wie unser node_modules-Ordner nach diesem Vorgang aussieht:

➜ nuxt-standalone git:(master) ✗ ls node_modules | wc -l && du -sh node_modules
     724
148M node_modules

Sieht es jetzt besser aus? Leider nicht, wir hätten etwas niedrigere Zahlen erwartet. Zum Glück hat das Team von Nuxt.js das geistige Wohl von Entwicklern im Auge behalten und eine experimentelle Funktion für uns vorbereitet, die jedoch beim Verfassen dieses Beitrags noch nicht in der Dokumentation verfügbar war. Lernen Sie den Standalone-Modus kennen.

Zweiter Schritt - Nuxt build --standalone

Wir haben von dieser Option durch die Suche auf GitHub erfahren. Alles begann mit einigen Kommentaren des Nutzers „clarkdo“, der zu Recht auf eines der möglichen Probleme bei der Verwendung von "nodeExternals" hinwies. Die Nuxt.js-Gemeinschaft musste nie lange auf eine Antwort warten, denn die prototype-Lösung erschien zwei Tage später. Aber was bringt das?

https://github.com/nuxt/nuxt.js/pull/4645

Step two - Nuxt build --standalone

Der Modus nuxt build --standalone erlaubt es uns, die Core-Pakete in die resultierende server.js-Datei „hineinzuziehen“, sodass wir potenziell keine weiteren Abhängigkeiten benötigen, um eine Nuxt.js-basierte Anwendung auszuführen. So können wir möglicherweise den Ordner node_modules loswerden.

Normal build:
Size of server.js: 22 Kib
Cost of dependencies (vue, vue-router, lodash (/omit), vue-no-ssr, debug): 7.4M
Cold start: 306.385ms
Memory usage: 29.9 MB (RSS: 106 MB)
Standalone build:
Size of server.js: 145 Kib
cold start: 306.550ms
Memory usage: 28.6 MB (RSS: 106 MB)

https://github.com/nuxt/nuxt.js/pull/4661

Leider nur möglicherweise, denn nach dem Entfernen von node_modules müssen Sie die Anwendung immer noch mit dem Befehl „yarn start“ starten, der wiederum das Paket @nuxtjs/cli benötigt. Sie können dieses Problem beheben, indem Sie die Anwendung mit folgendem Befehl starten:

“npx nuxt-start”

Auf diese Weise müssen Sie nicht alle Pakete lokal installieren. Der Befehl “npx” sucht zuerst nach Binärdateien in Ihren lokalen node_modules. Wenn er keine findet, sucht er nach global installierten Modulen, und als letzten Ausweg ruft er npm registry auf, um das betreffende Modul aus dem Internet herunterzuladen.

Mir ist klar, dass der npx-Befehl für die Produktion vielleicht nicht die beste Idee ist. Ohne eine spezifische Versionsdefinition werden Sie immer die neueste Version erhalten oder die Version, die Sie lokal haben. Daher sieht dieser Prozess je nach der von Ihnen verwendeten Bereitstellung möglicherweise anders aus. Wenn Sie Docker verwenden, können Sie eine bestimmte nuxt-start-Version global auf Docker installieren oder ein zusätzliches Skript vorbereiten, das nach dem Erstellen der Anwendung im Standalone-Modus alle node_modules entfernt und dann nur das benötigte nuxt-start-Modul installiert.

Eine Sache, die Sie bei der Entwicklung Ihrer Anwendung beachten sollten, ist die Verwendung von „buildModules” anstelle von „modules“ bei der Definition der Liste der verwendeten Module in nuxt.config.js. Der Unterschied zwischen den beiden besteht darin, dass „buildModules“ immer in den resultierenden Dateien enthalten sind, so dass die Anwendung schneller starten kann und die Größe von node_modules für die Produktionsbereitstellung reduziert wird. Sie müssen in der Dokumentation des zu installierenden Moduls nachsehen, ob es die Option „buildModules“ unterstützt. Mehr dazu finden Sie hier.

// Modules for dev and build (recommended) (https://go.nuxtjs.dev/config-modules)
buildModules: [
  // https://go.nuxtjs.dev/eslint
  '@nuxtjs/eslint-module',
  '@nuxtjs/pwa'
],

// Modules (https://go.nuxtjs.dev/config-modules)
modules: [
],

Obwohl der Autor der Lösung auf GitHub behauptet, dass es sich um eine experimentelle Option handelt, die zuerst sorgfältig getestet werden sollte, sind wir gerade dabei, unser erstes Projekt in diesem Modus in Produktion zu geben und haben keine Probleme festgestellt. Dadurch konnten wir die Größe unserer Container von durchschnittlich 800 MB auf etwa 65 MB reduzieren, was automatisch die Freigabezeit verkürzt und den Speicherplatzbedarf auf den Integrationsservern verringert hat.

Reduced Docker image size from 800MB to 65MB, enhancing deployment efficiency.