Im April dieses Jahres haben wir eine Übersetzung des
ersten Materials aus einer Reihe veröffentlicht, die sich einem verantwortungsvollen Ansatz bei der JavaScript-Entwicklung widmet. Dort reflektierte der Autor moderne Webtechnologien und deren rationellen Einsatz. Jetzt bieten wir Ihnen eine Übersetzung des zweiten Artikels aus dieser Reihe an. Es widmet sich einigen technischen Details zur Gestaltung von Webprojekten.

Habe eine Idee
Sie und Ihr Team haben begeistert die Idee einer vollständigen Überarbeitung der alternden Website des Unternehmens beworben. Ihre Anfragen erreichten die Führung, kamen sogar in die Sicht derjenigen ganz oben. Sie erhielten grünes Licht. Ihr Team machte sich begeistert an die Arbeit und zog Designer, Texter und andere Spezialisten an. Bald haben Sie einen neuen Code eingeführt.
Die Arbeit daran begann unschuldig. Der Befehl
npm install
ist hier, der Befehl
npm install
ist da. Sobald Sie sich umgesehen haben, wurden bereits Produktionsabhängigkeiten hergestellt, als wäre die Projektentwicklung ein wilder Alkohol, und Sie waren derjenige, dem es überhaupt egal war, was morgen früh sein würde.
Dann hast du angefangen.
Aber im Gegensatz zu den Folgen der verrücktesten Trinkparty begann das Schreckliche nicht am nächsten Morgen. Leider nicht am nächsten Morgen. Die Abrechnung erfolgte in Monaten. Sie nahm die unangenehme Form von leichter Übelkeit und Kopfschmerzen für Firmeninhaber und mittlere Manager an, die sich fragten, warum nach dem Start der neuen Website die Conversions und Einnahmen zurückgingen. Dann gewann die Katastrophe an Fahrt. Dies geschah, als der technische Direktor vom Wochenende zurückkam, das er irgendwo außerhalb der Stadt verbrachte. Er fragte sich, warum die Website des Unternehmens so langsam (wenn überhaupt) auf sein Handy geladen wurde.
Früher war es gut für alle. Jetzt sind andere dunkle Zeiten gekommen. Treffen Sie Ihren ersten Kater, nachdem Sie eine große Dosis JavaScript konsumiert haben.
Das ist nicht deine Schuld.
Während Sie versuchten, mit einem höllischen Kater fertig zu werden, klangen Worte wie „Ich habe es Ihnen gesagt“ für Sie wie ein wohlverdienter Verweis. Und wenn Sie zu diesem Zeitpunkt kämpfen könnten, könnten sie als Anlass für einen Kampf dienen.
Wenn es um die Folgen des rücksichtslosen Einsatzes von JavaScript geht, können Sie alles und jeden beschuldigen. Aber die Schuldigen zu suchen ist Zeitverschwendung. Das moderne Webgerät selbst erfordert, dass Unternehmen Probleme schneller lösen als ihre Konkurrenten. Ein solcher Druck bedeutet, dass wir, um unsere Produktivität so weit wie möglich zu steigern, wahrscheinlich alles ergreifen werden. Dies bedeutet, dass wir mit hoher Wahrscheinlichkeit (obwohl dies nicht als unvermeidlich bezeichnet werden kann) Anwendungen erstellen, in denen es viele Exzesse geben wird, und höchstwahrscheinlich Muster verwenden werden, die die Leistung und Verfügbarkeit von Anwendungen beeinträchtigen.
Webentwicklung ist keine leichte Aufgabe. Das ist ein langer Job. Es wird selten beim ersten Versuch gut ausgeführt. Das Beste an dieser Arbeit ist jedoch, dass wir nicht gleich zu Beginn alles perfekt machen müssen. Wir können Projekte nach dem Start verbessern, und tatsächlich ist dieses Material diesem Thema gewidmet, dem zweiten in einer Reihe von Artikeln über einen verantwortungsvollen Ansatz bei der JS-Entwicklung. Perfektion ist ein sehr weit entferntes Ziel. In der Zwischenzeit wollen wir uns mit dem JavaScript-Kater befassen, indem wir sozusagen die Skripterstellung verbessern
auf der Website in naher Zukunft.
Wir beschäftigen uns mit allgemeinen Problemen
Dies mag wie ein mechanischer Ansatz zur Lösung von Problemen erscheinen, aber gehen Sie zuerst eine Liste typischer Probleme und Möglichkeiten durch, um mit ihnen umzugehen. In großen Entwicklungsteams werden diese Dinge oft vergessen. Dies gilt insbesondere für Teams, die mit mehreren Repositorys arbeiten oder für ihre Projekte keine optimierte Vorlage verwenden.
▍ Wenden Sie den Baumschüttelalgorithmus an
Überprüfen Sie zunächst, ob Ihre Tools für die Implementierung des
Tree-Shaking- Algorithmus konfiguriert sind. Wenn Sie dieses Konzept noch nicht kennengelernt haben, schauen Sie sich dieses Material von
mir an , das ich letztes Jahr geschrieben habe. Wenn wir die Funktionsweise dieses Algorithmus auf den Punkt bringen, können wir sagen, dass aufgrund seiner Verwendung in den Produktionsbaugruppen der Anwendung nicht die Pakete enthalten sind, die zwar in das Projekt importiert, aber nicht darin verwendet werden.
Die Implementierung des Tree-Shaking-Algorithmus ist eine Standardfunktion moderner Bundler wie
Webpack ,
Rollup oder
Parcel .
Grunzen oder
Schlucken sind Task-Manager. Sie machen das nicht. Der Task-Manager erstellt im Gegensatz zum Bundler kein
Abhängigkeitsdiagramm . Der Task-Manager ist mit den erforderlichen Plug-Ins beschäftigt und führt separate Manipulationen an den darauf übertragenen Dateien durch. Die Funktionalität von Task-Managern kann mithilfe von Plugins erweitert werden, sodass sie JavaScript mithilfe von Bundlern verarbeiten können. Wenn die Erweiterung der Funktionen des Task-Managers in diese Richtung ein Problem darstellt, müssen Sie wahrscheinlich die Codebasis manuell überprüfen und nicht verwendeten Code daraus entfernen.
Damit der Baumschüttelalgorithmus effizient funktioniert, müssen die folgenden Bedingungen erfüllt sein:
- Anwendungscode und installierte Pakete sollten als ES6-Module dargestellt werden . Die Verwendung des Tree-Shaking-Algorithmus für CommonJS- Module ist nahezu unmöglich.
- Ihr Bundler sollte ES6-Module während der Erstellung des Projekts nicht in Module eines anderen Formats umwandeln. Wenn dies in der Toolchain geschieht, die Babel verwendet, muss @ Babel / present-env die folgenden Module haben: false . Dies führt dazu, dass der ES6-Code nicht in Code konvertiert wird, der CommonJS verwendet.
Wenn beim Erstellen Ihres Projekts plötzlich der Baumschüttelalgorithmus nicht angewendet wird, kann die Einbeziehung dieses Mechanismus die Situation verbessern. Natürlich variiert die Wirksamkeit dieses Algorithmus von Projekt zu Projekt. Darüber hinaus hängt die Möglichkeit seiner Verwendung davon ab, ob die importierten Module
Nebenwirkungen haben . Dies kann die Fähigkeit des Bündlers beeinträchtigen, die Aufnahme unnötiger importierter Module in die Baugruppe zu vermeiden.
▍ Teilen Sie den Code in Teile
Es ist sehr wahrscheinlich, dass Sie bereits eine Form der
Codetrennung verwenden . Sie sollten jedoch überprüfen, wie dies gemacht wird. Unabhängig davon, wie Sie den Code trennen, möchte ich Ihnen anbieten, sich die folgenden zwei sehr wertvollen Fragen zu stellen:
- Entfernen Sie doppelten Code von den Eingabepunkten ?
- Laden Sie träge alles, was auf diese Weise mit dynamischen Importen geladen werden kann?
Diese Probleme sind wichtig, da die Reduzierung der Menge an redundantem Code ein entscheidendes Leistungselement ist. Das verzögerte Laden des Codes verbessert auch die Leistung, indem die Menge an JavaScript-Code reduziert wird, der Teil der Seite ist und beim Laden geladen wird. Wenn wir über die Analyse des Projekts auf das Vorhandensein von redundantem Code sprechen, können Sie hierfür ein Tool wie den Bundle Buddy verwenden. Wenn Ihr Projekt ein Problem damit hat, werden Sie über dieses Tool informiert.
Das Bundle Buddy-Tool kann Informationen zur Webpack-Kompilierung überprüfen und herausfinden, wie viel des gleichen Codes in Ihren Bundles verwendet wirdWenn wir über das verzögerte Laden von Materialien sprechen, kann es schwierig sein, herauszufinden, wo nach Möglichkeiten gesucht werden kann, diese Optimierung anzuwenden. Wenn ich in einem vorhandenen Projekt nach der Möglichkeit suche, das verzögerte Laden zu verwenden, suche ich in der Codebasis nach Stellen, an denen Benutzerinteraktionen mit dem Code stattfinden. Dies können beispielsweise Maus- oder Tastaturereignishandler sowie ähnliche Dinge sein. Jeder Code, für dessen Ausführung eine Benutzeraktion erforderlich ist, ist ein guter Kandidat für die Anwendung des Befehls dynamic
import()
.
Das Laden von Skripten bei Bedarf birgt natürlich das Risiko spürbarer Verzögerungen beim Wechsel des Systems in den interaktiven Modus. Bevor das Programm interaktiv mit dem Benutzer interagieren kann, müssen Sie das entsprechende Skript herunterladen. Wenn Sie die übertragene Datenmenge nicht stört, sollten Sie den Ressourcenhinweis
rel = prefetch verwenden , um solche Skripte mit niedriger Priorität zu laden. Solche Ressourcen konkurrieren nicht um Bandbreite mit kritischen Ressourcen. Wenn der Browser des Benutzers
rel=prefetch
, ist die Verwendung dieses Tooltips nur von Vorteil. Wenn nicht, passiert nichts Schlimmes, da Browser Markups, die sie nicht verstehen, einfach ignorieren.
▍Verwenden Sie die Option "Webpack externals", um Ressourcen auf fremden Servern zu markieren
Idealerweise sollten Sie so viele Abhängigkeiten Ihrer Site wie möglich auf Ihren eigenen Servern hosten. Wenn Sie aus irgendeinem Grund ohne Optionen Abhängigkeiten von den Servern anderer Personen herunterladen müssen, fügen Sie diese in den Webpack-Einstellungen in den
externen Block ein. Wenn dies nicht erfolgt, kann dies bedeuten, dass Besucher Ihrer Website sowohl den von Ihnen gehosteten Code als auch denselben Code von den Servern anderer Personen herunterladen.
Sehen Sie sich eine hypothetische Situation an, in der so etwas Ihrer Ressource schaden könnte. Angenommen, Ihre Site lädt die Lodash-Bibliothek von einer öffentlichen CDN-Ressource herunter. Sie haben Lodash auch für lokale Entwicklungszwecke im Projekt installiert. Wenn Sie jedoch in den Webpack-Einstellungen nicht angeben, dass Lodash eine externe Abhängigkeit ist, lädt Ihr Produktionscode die Bibliothek aus dem CDN, wird jedoch gleichzeitig in das auf Ihrem Server gehostete Bundle aufgenommen.
Wenn Sie mit Bündlern gut vertraut sind, scheint Ihnen dies alles alltägliche Wahrheiten zu sein. Aber ich habe gesehen, wie diese Dinge nicht aufpassen. Nehmen Sie sich daher nicht die Zeit, Ihr Projekt auf die oben genannten Probleme zu überprüfen.
Wenn Sie es nicht für erforderlich halten, Ihre von
Drittentwicklern erstellten Abhängigkeiten selbst zu
hosten , sollten Sie
DNS -Prefetch- ,
Preconnect- oder vielleicht sogar
Preload- Hinweise verwenden. Dies kann den
TTI- Wert (Time To Interactive, Site Time to First Interactive) verringern. Wenn JavaScript-Funktionen erforderlich sind, um den Inhalt der Website anzuzeigen,
wird auch der
Geschwindigkeitsindex der Website benötigt.
Kleinere alternative Bibliotheken und reduzierter Overhead auf Benutzersystemen
Was als "
Userland JavaScript " (vom Benutzer entwickelte JS-Bibliotheken) bezeichnet wird, scheint ein obszöner riesiger Süßwarenladen zu sein. All diese Open-Source-Pracht und Vielfalt inspiriert uns Entwickler mit Ehrfurcht. Mit Frameworks und Bibliotheken können wir unsere Anwendungen erweitern und sie schnell mit Funktionen ausstatten, die zur Lösung einer Vielzahl von Problemen beitragen. Wenn wir die gleiche Funktionalität selbst implementieren müssten, würde dies viel Zeit und Energie kosten.
Obwohl ich persönlich eine aggressive Minimierung der Verwendung von Client-Frameworks und -Bibliotheken in meinen Projekten befürworte, kann ich deren enormen Wert und Nutzen nur erkennen. Trotzdem sollten wir bei der Installation neuer Abhängigkeiten im Projekt jede einzelne mit einem gewissen Misstrauen behandeln. Wenn wir bereits etwas erstellt und gestartet haben, dessen Betrieb von vielen installierten Abhängigkeiten abhängt, bedeutet dies, dass wir die zusätzliche Belastung des Systems in Kauf nehmen, die durch all dies entsteht. Es ist wahrscheinlich, dass nur Paketentwickler dieses Problem lösen können, indem sie ihre Entwicklung optimieren. Ist es so?
Vielleicht ist das so, aber vielleicht auch nicht. Dies hängt von den verwendeten Abhängigkeiten ab. Zum Beispiel ist React eine äußerst beliebte Bibliothek.
Preact ist jedoch eine
sehr kleine Alternative zu React, die dem Entwickler fast die gleichen APIs bietet und mit vielen React-Add-Ons kompatibel bleibt.
Luxon und
date-fns sind Alternativen zu
moment.js , viel kompakter als diese Bibliothek, die
nicht so klein ist .
In Bibliotheken wie
Lodash finden Sie viele nützliche Methoden. Einige von ihnen lassen sich jedoch leicht durch Standard-ES6-Methoden ersetzen. Beispielsweise kann die Lodash-
Kompaktmethode durch die Standardmethode für
Filterarrays ersetzt werden.
Viele andere Lodash-Methoden können auch sicher durch Standardmethoden ersetzt werden. Der Vorteil dieses Ersatzes besteht darin, dass wir die gleichen Funktionen wie bei der Verwendung der Bibliothek erhalten, jedoch eine ziemlich große Abhängigkeit beseitigen.
Was auch immer Sie verwenden, die allgemeine Idee bleibt dieselbe: Fragen Sie, ob Ihre Wahl kompaktere Alternativen bietet. Finden Sie heraus, ob Sie dieselben Probleme mit Standard-Sprachwerkzeugen lösen können. Vielleicht werden Sie angenehm überrascht sein, wie wenig Sie unternehmen müssen, um die Größe der Anwendung und die unnötige Last, die sie auf Benutzersysteme ausübt, ernsthaft zu reduzieren.
Verwenden Sie Technologien zum unterschiedlichen Laden von Skripten
Wahrscheinlich befindet sich Babel in Ihrer Toolchain. Dieses Tool wird verwendet, um ES6-kompatiblen Quellcode in Code umzuwandeln, den ältere Browser ausführen können. Bedeutet dies, dass wir dazu verdammt sind, selbst Browsern, die sie nicht benötigen, riesige Bündel zu geben, bis alle alten Browser einfach verschwinden?
Natürlich nicht ! Das unterschiedliche Laden von Ressourcen hilft, dieses Problem zu umgehen, indem zwei verschiedene Assemblys basierend auf ES6-Code erstellt werden:
- Die erste Assembly enthält alle Codekonvertierungen und Polyfills, die erforderlich sind, damit Ihre Site in älteren Browsern funktioniert. Wahrscheinlich geben Sie Ihren Kunden jetzt diese spezielle Baugruppe.
- Die zweite Assembly enthält entweder ein Minimum an Code- und Polyfill-Konvertierungen oder verzichtet überhaupt darauf. Es ist für moderne Browser konzipiert. Dies ist eine Baugruppe, die Sie möglicherweise nicht haben. Zumindest noch nicht.
Um die Technologie der Differenzialbelastung von Baugruppen nutzen zu können, müssen Sie ein wenig arbeiten. Ich werde hier nicht auf Details eingehen. Ich werde einen besseren
Link zu meinem Material geben, in dem eine der Möglichkeiten zur Implementierung dieser Technologie erläutert wird. Das Wesentliche dabei ist, dass Sie Ihre Build-Konfiguration so ändern können, dass während des Builds des Projekts eine zusätzliche Version des JS-Bundles Ihrer Site erstellt wird. Dieses zusätzliche Paket ist kleiner als das Hauptpaket. Es ist nur für moderne Browser gedacht. Das Beste daran ist, dass Sie mit diesem Ansatz die Größe des Bundles optimieren und gleichzeitig absolut nichts von den Fähigkeiten des Projekts opfern können. Abhängig vom Anwendungscode kann das Einsparen der Bundle-Größe erheblich sein.
Analyse von Bundles für ältere Browser (links) und Bundles für neue Browser (rechts). Die Bundle-Studie wurde mit dem Webpack-Bundle-Analyzer durchgeführt. Hier ist die Vollversion dieses Bildes.Mit dem folgenden Trick ist es am einfachsten, verschiedenen Browsern unterschiedliche Bundles zuzuweisen. Es funktioniert gut in modernen Browsern:
<!-- : --> <script type="module" src="/js/app.mjs"></script> <!-- : --> <script defer nomodule src="/js/app.js"></script>
Leider hat dieser Ansatz Nachteile. Veraltete Browser wie IE11 und sogar relativ moderne wie die Edge-Versionen 15-18 laden beide Bundles. Wenn Sie bereit sind, es zu ertragen - dann verwenden Sie diese Technik und machen Sie sich um nichts Sorgen.
Auf der anderen Seite müssen Sie sich etwas einfallen lassen, falls Sie sich Sorgen über die
Auswirkungen der Tatsache auf die Leistung Ihrer Anwendung machen, dass ältere Browser beide Bundles herunterladen müssen. Hier ist eine mögliche Lösung für dieses Problem, bei der die Skriptinjektion verwendet wird (anstelle des oben verwendeten
<script>
-Tags). Es vermeidet das doppelte Laden von Bundles durch geeignete Browser. Hier ist, worüber wir sprechen:
var scriptEl = document.createElement("script"); if ("noModule" in scriptEl) {
Dieses Skript geht davon aus, dass der Browser das
Konstrukt type="module"
versteht, wenn er das Attribut
nomodule im
Skriptelement unterstützt. Dies stellt sicher, dass ältere Browser nur für sie entworfene Skripte erhalten und moderne Browser für sie entworfene Skripte. Beachten Sie jedoch, dass dynamisch eingebettete Skripte standardmäßig asynchron geladen werden. Wenn die Reihenfolge des Ladens von Abhängigkeiten für Sie wichtig ist, setzen Sie das
asynchrone Attribut daher auf
false
.
Weniger vermitteln
Ich werde Babel hier nicht angreifen. Dieses Tool ist in der modernen Webentwicklung notwendig, aber es ist eine sehr eigensinnige Einheit. Babel fügt dem generierten Code eine Menge Dinge hinzu, von denen der Entwickler möglicherweise nichts weiß. Daher werden Sie es nicht bereuen, wenn Sie in die Eingeweide von Babel schauen und genau herausfinden, was er tut. Insbesondere die Kenntnis der internen Mechanismen von Babel macht deutlich, dass kleine Änderungen in der Art und Weise, wie jemand Code schreibt, sich positiv auf das auswirken können, was Babel generiert.
Weniger vermittelnDas ist es, worüber wir sprechen. Beispielsweise sind
die Standardoptionen eine sehr praktische Funktion von ES6, die Sie möglicherweise bereits verwenden:
function logger(message, level = "log") { console[level](message); }
Hier lohnt es sich, auf den Parameter
level
zu achten, dessen Standardwert das Zeichenfolgenprotokoll ist. Dies bedeutet, dass wir, wenn wir
console.log
mit der
logger
Wrapper-Funktion aufrufen
console.log
, keine
level
an diese Funktion übergeben müssen. Praktisch, richtig? All dies ist gut - außer dem Code, den Babel bei der Transformation dieser Funktion erhält:
function logger(message) { var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "log"; console[level](message); }
Dies ist ein Beispiel dafür, wie trotz der Tatsache, dass wir von guten Absichten geleitet werden, der Komfort, den Babel bietet, zu negativen Konsequenzen führen kann. Was nur wenige Zeichen im Quellcode waren, wurde in der Produktionsversion des Programms zu einer viel längeren Konstruktion. - , ,
arguments
.
, ,
? , Babel :
, Babel
@babel/preset-env ,
. , , , , , ! — «» (
true
loose ). — , , , , , . «»
, Babel , .
, «» , , Babel :
, — JavaScript, , .
spread ,
,
.
— :
- — @babel/runtime @babel/plugin-transform-runtime , , Babel .
- , . @babel/polyfill . , babel /preset-env useBuiltins
usage
.
, , , , , . ,
JSX , , , . , , . , , . , Babel — . , Babel. , .
: —
, . , JavaScript-, , . , , . - .
. , , , , , , , .
, , , , , , , . - — . , , , . . , , , , . , , , .
Liebe Leser! JS-?
