Vermeidung der Fallstricke von automatisch eingebettetem Code

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Die übermäßige Verwendung von Inline-CSS- oder JS-Code im Gegensatz zur Bereitstellung von Code über statische Ressourcen kann die Leistung der Website beeinträchtigen. In diesem Artikel lernen wir, wie man stattdessen dynamischen Code über statische Dateien lädt und so die Nachteile von zu viel Inline-Code vermeidet.

Inlining ist der Vorgang, bei dem der Inhalt von Dateien direkt in das HTML-Dokument eingefügt wird: CSS-Dateien können in ein style eingebettet werden, und JavaScript-Dateien können in ein script eingebettet werden:

 <style> /* CSS contents here */ </style> <script> /* JS contents here */ </script>

Indem der Code bereits in der HTML-Ausgabe ausgegeben wird, vermeidet Inlining Anfragen, die das Rendern blockieren, und führt den Code aus, bevor die Seite gerendert wird. Als solches ist es nützlich, um die wahrgenommene Leistung der Website zu verbessern (dh die Zeit, die es dauert, bis eine Seite benutzbar wird). Zum Beispiel können wir den Datenpuffer verwenden, der sofort beim Laden der Website (etwa 14 KB) für Inline bereitgestellt wird die kritischen Stile, einschließlich Stile von Inhalten "above the fold" (wie auf der vorherigen Smashing Magazine-Website), und Schriftgrößen sowie Layoutbreiten und -höhen, um ein sprunghaftes erneutes Rendern des Layouts zu vermeiden, wenn die restlichen Daten geliefert werden .

Wenn es jedoch übertrieben wird, kann Inlining-Code auch negative Auswirkungen auf die Leistung der Website haben: Da der Code nicht zwischengespeichert werden kann, wird derselbe Inhalt wiederholt an den Client gesendet und kann nicht von Service Workern vorab zwischengespeichert werden zwischengespeichert und von einem Content Delivery Network abgerufen. Darüber hinaus gelten Inline-Skripte bei der Implementierung einer Content Security Policy (CSP) als nicht sicher. Dann ist es eine vernünftige Strategie, die kritischen Teile von CSS und JS zu integrieren, die das Laden der Website beschleunigen, aber ansonsten so viel wie möglich vermeiden.

Mit dem Ziel, Inlining zu vermeiden, werden wir in diesem Artikel untersuchen, wie Inline-Code in statische Assets konvertiert werden kann: Anstatt den Code in der HTML-Ausgabe zu drucken, speichern wir ihn auf der Festplatte (erstellen effektiv eine statische Datei) und fügen das entsprechende <script> oder <link> Tag, um die Datei zu laden.

Lass uns anfangen!

Empfohlene Lektüre : WordPress-Sicherheit als Prozess

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Wann man Inlining vermeiden sollte

Es gibt kein Zauberrezept, um festzustellen, ob ein Code inliniert werden muss oder nicht, aber es kann ziemlich offensichtlich sein, wenn ein Code nicht inliniert werden darf: wenn es sich um einen großen Codeblock handelt und wenn er nicht sofort benötigt wird.

Beispielsweise integrieren WordPress-Sites die JavaScript-Vorlagen zum Rendern des Medienmanagers (aufrufbar auf der Seite „Medienbibliothek“ unter /wp-admin/upload.php ) und drucken eine beträchtliche Menge an Code:

Ein Screenshot des Quellcodes für die Seite Medienbibliothek
Vom WordPress Media Manager eingebundene JavaScript-Vorlagen.

Mit vollen 43 KB ist die Größe dieses Codestücks nicht zu vernachlässigen, und da es sich am Ende der Seite befindet, wird es nicht sofort benötigt. Daher wäre es sehr sinnvoll, diesen Code stattdessen über statische Assets bereitzustellen oder ihn in die HTML-Ausgabe zu drucken.

Sehen wir uns als Nächstes an, wie Inline-Code in statische Assets umgewandelt wird.

Auslösen der Erstellung statischer Dateien

Wenn die Inhalte (die einzufügenden) aus einer statischen Datei stammen, müssen Sie nicht viel tun, außer einfach diese statische Datei anzufordern, anstatt den Code einzufügen.

Für dynamischen Code müssen wir jedoch planen, wie/wann die statische Datei mit ihrem Inhalt generiert wird. Wenn die Site beispielsweise Konfigurationsoptionen bietet (z. B. das Ändern des Farbschemas oder des Hintergrundbilds), wann sollte die Datei mit den neuen Werten generiert werden? Wir haben folgende Möglichkeiten, die statischen Dateien aus dem dynamischen Code zu erstellen:

  1. Auf Anfrage
    Wenn ein Benutzer zum ersten Mal auf den Inhalt zugreift.
  2. Bei Änderung
    Wenn sich die Quelle für den dynamischen Code (z. B. ein Konfigurationswert) geändert hat.

Betrachten wir zuerst eine Anfrage. Wenn ein Benutzer zum ersten Mal auf die Site zugreift, sagen wir über /index.html , existiert die statische Datei (z. B. header-colors.css ) noch nicht, also muss sie dann generiert werden. Der Ablauf der Ereignisse ist folgender:

  1. Der Benutzer fordert /index.html ;
  2. Bei der Verarbeitung der Anfrage prüft der Server, ob die Datei header-colors.css existiert. Da dies nicht der Fall ist, erhält es den Quellcode und generiert die Datei auf der Festplatte.
  3. Es gibt eine Antwort an den Client zurück, einschließlich des Tags <link rel="stylesheet" type="text/css" href="/staticfiles/header-colors.css">
  4. Der Browser ruft alle in der Seite enthaltenen Ressourcen ab, einschließlich header-colors.css ;
  5. Bis dahin existiert diese Datei, also wird sie bedient.

Die Abfolge der Ereignisse könnte jedoch auch anders sein, was zu einem unbefriedigenden Ergebnis führen würde. Zum Beispiel:

  1. Der Benutzer fordert /index.html ;
  2. Diese Datei wird bereits vom Browser (oder einem anderen Proxy oder von Service Workern) zwischengespeichert, sodass die Anfrage niemals an den Server gesendet wird.
  3. Der Browser ruft alle auf der Seite enthaltenen Ressourcen ab, einschließlich header-colors.css . Dieses Bild wird jedoch nicht im Browser zwischengespeichert, sodass die Anfrage an den Server gesendet wird;
  4. Der Server hat noch keine header-colors.css generiert (z. B. wurde er gerade neu gestartet);
  5. Es wird ein 404 zurückgegeben.

Alternativ könnten wir header-colors.css nicht beim Anfordern /index.html , sondern beim Anfordern /header-colors.css selbst. Da diese Datei jedoch anfänglich nicht existiert, wird die Anfrage bereits als 404 behandelt. Auch wenn wir uns hacken könnten, indem wir die Header ändern, um den Statuscode auf 200 zu ändern, und den Inhalt des Bildes zurückgeben, Das ist eine schreckliche Art, Dinge zu tun, also werden wir diese Möglichkeit nicht in Betracht ziehen (wir sind viel besser als das!)

Damit bleibt nur eine Option: die statische Datei zu generieren, nachdem sich ihre Quelle geändert hat.

Erstellen der statischen Datei, wenn sich die Quelle ändert

Bitte beachten Sie, dass wir dynamischen Code sowohl aus benutzerabhängigen als auch aus standortabhängigen Quellen erstellen können. Wenn das Design beispielsweise das Ändern des Hintergrundbilds der Website ermöglicht und diese Option vom Administrator der Website konfiguriert wird, kann die statische Datei als Teil des Bereitstellungsprozesses generiert werden. Wenn die Website ihren Benutzern andererseits erlaubt, das Hintergrundbild für ihre Profile zu ändern, muss die statische Datei zur Laufzeit generiert werden.

Kurz gesagt, wir haben diese beiden Fälle:

  1. Benutzer Konfiguration
    Der Prozess muss ausgelöst werden, wenn der Benutzer eine Konfiguration aktualisiert.
  2. Site-Konfiguration
    Der Prozess muss ausgelöst werden, wenn der Administrator eine Konfiguration für die Site aktualisiert oder bevor die Site bereitgestellt wird.

Wenn wir die beiden Fälle unabhängig voneinander betrachten würden, könnten wir für #2 den Prozess auf jedem gewünschten Technologie-Stack entwerfen. Wir wollen aber nicht zwei unterschiedliche Lösungen implementieren, sondern eine einzigartige Lösung, die beide Fälle bewältigen kann. Und da der Prozess zum Generieren der statischen Datei ab Nr. 1 auf der laufenden Website ausgelöst werden muss, ist es zwingend erforderlich, diesen Prozess um denselben Technologie-Stack herum zu entwerfen, auf dem die Website ausgeführt wird.

Beim Entwerfen des Prozesses muss unser Code die spezifischen Umstände von Nr. 1 und Nr. 2 berücksichtigen:

  • Versionierung
    Auf die statische Datei muss mit einem „Version“-Parameter zugegriffen werden, um die vorherige Datei beim Erstellen einer neuen statischen Datei ungültig zu machen. Während #2 einfach die gleiche Versionierung wie die Site haben könnte, muss #1 für jeden Benutzer eine dynamische Version verwenden, möglicherweise in der Datenbank gespeichert.
  • Speicherort der generierten Datei
    #2 generiert eine eindeutige statische Datei für die gesamte Site (z. B. /staticfiles/header-colors.css ), während #1 eine statische Datei für jeden Benutzer erstellt (z. B. /staticfiles/users/leo/header-colors.css ).
  • Auslöseereignis
    Während für #1 die statische Datei zur Laufzeit ausgeführt werden muss, kann sie für #2 auch als Teil eines Build-Prozesses in unserer Staging-Umgebung ausgeführt werden.
  • Bereitstellung und Verteilung
    Statische Dateien in Nr. 2 können nahtlos in das Bereitstellungspaket der Site integriert werden und stellen keine Herausforderungen dar; statische Dateien in Nr. 1 können dies jedoch nicht, daher muss der Prozess zusätzliche Probleme behandeln, z. B. mehrere Server hinter einem Load Balancer (werden die statischen Dateien nur auf einem Server oder auf allen erstellt und wie?).

Lassen Sie uns als Nächstes den Prozess entwerfen und implementieren. Für jede zu generierende statische Datei müssen wir ein Objekt erstellen, das die Metadaten der Datei enthält, seinen Inhalt aus den dynamischen Quellen berechnen und schließlich die statische Datei auf der Festplatte speichern. Als Anwendungsfall für die folgenden Erläuterungen werden wir die folgenden statischen Dateien generieren:

  1. header-colors.css , mit etwas Stil aus in der Datenbank gespeicherten Werten
  2. welcomeuser-data.js , das ein JSON-Objekt mit Benutzerdaten unter einer Variablen enthält: window.welcomeUserData = {name: "Leo"}; .

Im Folgenden beschreibe ich den Prozess zum Generieren der statischen Dateien für WordPress, für den wir den Stack auf PHP- und WordPress-Funktionen basieren müssen. Die Funktion zum Generieren der statischen Dateien vor der Bereitstellung kann durch Laden einer speziellen Seite ausgelöst werden, die den [create_static_files] wie ich in einem früheren Artikel beschrieben habe.

Weitere empfohlene Lektüre : Making A Service Worker: A Case Study

Darstellen der Datei als Objekt

Wir müssen eine Datei als PHP-Objekt mit allen entsprechenden Eigenschaften modellieren, damit wir die Datei sowohl an einem bestimmten Ort auf der Festplatte speichern können (z. B. entweder unter /staticfiles/ oder /staticfiles/users/leo/ ) als auch wissen, wie man sie anfordert Datei folglich. Dazu erstellen wir eine Schnittstellenressource, die sowohl die Metadaten der Datei (Dateiname, Verzeichnis, Typ: „css“ oder „ Resource “, Version und Abhängigkeiten von anderen Ressourcen) als auch ihren Inhalt zurückgibt.

 interface Resource { function get_filename(); function get_dir(); function get_type(); function get_version(); function get_dependencies(); function get_content(); }

Um den Code wartbar und wiederverwendbar zu machen, folgen wir den SOLID-Prinzipien, für die wir ein Objektvererbungsschema für Ressourcen festlegen, um nach und nach Eigenschaften hinzuzufügen, beginnend mit der abstrakten Klasse ResourceBase , von der alle unsere Ressourcenimplementierungen erben:

 abstract class ResourceBase implements Resource { function get_dependencies() { // By default, a file has no dependencies return array(); } }

Nach SOLID erstellen wir Unterklassen, wenn sich Eigenschaften unterscheiden. Wie bereits erwähnt, unterscheiden sich der Speicherort der generierten statischen Datei und die Versionierung, um sie anzufordern, je nachdem, ob es sich bei der Datei um die Benutzer- oder Site-Konfiguration handelt:

 abstract class UserResourceBase extends ResourceBase { function get_dir() { // A different file and folder for each user $user = wp_get_current_user(); return "/staticfiles/users/{$user->user_login}/"; } function get_version() { // Save the resource version for the user under her meta data. // When the file is regenerated, must execute `update_user_meta` to increase the version number $user_id = get_current_user_id(); $meta_key = "resource_version_".$this->get_filename(); return get_user_meta($user_id, $meta_key, true); } } abstract class SiteResourceBase extends ResourceBase { function get_dir() { // All files are placed in the same folder return "/staticfiles/"; } function get_version() { // Same versioning as the site, assumed defined under a constant return SITE_VERSION; } }

Schließlich implementieren wir auf der letzten Ebene die Objekte für die Dateien, die wir generieren möchten, indem wir den Dateinamen, den Dateityp und den dynamischen Code durch die Funktion get_content :

 class HeaderColorsSiteResource extends SiteResourceBase { function get_filename() { return "header-colors"; } function get_type() { return "css"; } function get_content() { return sprintf( " .site-title a { color: #%s; } ", esc_attr(get_header_textcolor()) ); } } class WelcomeUserDataUserResource extends UserResourceBase { function get_filename() { return "welcomeuser-data"; } function get_type() { return "js"; } function get_content() { $user = wp_get_current_user(); return sprintf( "window.welcomeUserData = %s;", json_encode( array( "name" => $user->display_name ) ) ); } }

Damit haben wir die Datei als PHP-Objekt modelliert. Als nächstes müssen wir es auf der Festplatte speichern.

Speichern der statischen Datei auf der Festplatte

Das Speichern einer Datei auf der Festplatte kann einfach durch die nativen Funktionen der Sprache erreicht werden. Im Fall von PHP wird dies durch die Funktion fwrite . Darüber hinaus erstellen wir eine Utility-Klasse ResourceUtils mit Funktionen, die den absoluten Pfad zur Datei auf der Festplatte sowie ihren Pfad relativ zum Stammverzeichnis der Site bereitstellen:

 class ResourceUtils { protected static function get_file_relative_path($fileObject) { return $fileObject->get_dir().$fileObject->get_filename().".".$fileObject->get_type(); } static function get_file_path($fileObject) { // Notice that we must add constant WP_CONTENT_DIR to make the path absolute when saving the file return WP_CONTENT_DIR.self::get_file_relative_path($fileObject); } } class ResourceGenerator { static function save($fileObject) { $file_path = ResourceUtils::get_file_path($fileObject); $handle = fopen($file_path, "wb"); $numbytes = fwrite($handle, $fileObject->get_content()); fclose($handle); } }

Wenn sich dann die Quelle ändert und die statische Datei neu generiert werden muss, führen wir ResourceGenerator::save aus und übergeben das Objekt, das die Datei darstellt, als Parameter. Der folgende Code generiert die Dateien „header-colors.css“ und „welcomeuser-data.js“ neu und speichert sie auf der Festplatte:

 // When need to regenerate header-colors.css, execute: ResourceGenerator::save(new HeaderColorsSiteResource()); // When need to regenerate welcomeuser-data.js, execute: ResourceGenerator::save(new WelcomeUserDataUserResource());

Sobald sie vorhanden sind, können wir Dateien in die Warteschlange stellen, die über die Tags <script> und <link> geladen werden sollen.

Einreihen der statischen Dateien

Das Einreihen der statischen Dateien unterscheidet sich nicht vom Einreihen einer Ressource in WordPress: durch die Funktionen wp_enqueue_script und wp_enqueue_style . Dann iterieren wir einfach alle Objektinstanzen und verwenden den einen oder anderen Hook, je nachdem, ob ihr get_type() -Wert entweder "js" oder "css" ist.

Wir fügen zuerst Hilfsfunktionen hinzu, um die URL der Datei bereitzustellen und um mitzuteilen, ob der Typ entweder JS oder CSS ist:

 class ResourceUtils { // Continued from above... static function get_file_url($fileObject) { // Add the site URL before the file path return get_site_url().self::get_file_relative_path($fileObject); } static function is_css($fileObject) { return $fileObject->get_type() == "css"; } static function is_js($fileObject) { return $fileObject->get_type() == "js"; } }

Eine Instanz der Klasse ResourceEnqueuer enthält alle Dateien, die geladen werden müssen; Wenn sie aufgerufen werden, führen ihre Funktionen enqueue_scripts und enqueue_styles das Enqueuing durch, indem sie die entsprechenden WordPress-Funktionen ausführen ( wp_enqueue_script bzw. wp_enqueue_style ):

 class ResourceEnqueuer { protected $fileObjects; function __construct($fileObjects) { $this->fileObjects = $fileObjects; } protected function get_file_properties($fileObject) { $handle = $fileObject->get_filename(); $url = ResourceUtils::get_file_url($fileObject); $dependencies = $fileObject->get_dependencies(); $version = $fileObject->get_version(); return array($handle, $url, $dependencies, $version); } function enqueue_scripts() { $jsFileObjects = array_map(array(ResourceUtils::class, 'is_js'), $this->fileObjects); foreach ($jsFileObjects as $fileObject) { list($handle, $url, $dependencies, $version) = $this->get_file_properties($fileObject); wp_register_script($handle, $url, $dependencies, $version); wp_enqueue_script($handle); } } function enqueue_styles() { $cssFileObjects = array_map(array(ResourceUtils::class, 'is_css'), $this->fileObjects); foreach ($cssFileObjects as $fileObject) { list($handle, $url, $dependencies, $version) = $this->get_file_properties($fileObject); wp_register_style($handle, $url, $dependencies, $version); wp_enqueue_style($handle); } } }

Schließlich instanziieren wir ein Objekt der Klasse ResourceEnqueuer mit einer Liste der PHP-Objekte, die jede Datei darstellen, und fügen einen WordPress-Hook hinzu, um das Enqueuing auszuführen:

 // Initialize with the corresponding object instances for each file to enqueue $fileEnqueuer = new ResourceEnqueuer( array( new HeaderColorsSiteResource(), new WelcomeUserDataUserResource() ) ); // Add the WordPress hooks to enqueue the resources add_action('wp_enqueue_scripts', array($fileEnqueuer, 'enqueue_scripts')); add_action('wp_print_styles', array($fileEnqueuer, 'enqueue_styles'));

Das war's: In die Warteschlange eingereiht, werden die statischen Dateien beim Laden der Seite im Client angefordert. Es ist uns gelungen, das Drucken von Inline-Code zu vermeiden und stattdessen statische Ressourcen zu laden.

Als nächstes können wir mehrere Verbesserungen für zusätzliche Leistungssteigerungen anwenden.

Empfohlene Lektüre : Eine Einführung in das automatisierte Testen von WordPress-Plugins mit PHPUnit

Bündeln von Dateien

Obwohl HTTP/2 die Notwendigkeit zum Bündeln von Dateien reduziert hat, macht es die Website immer noch schneller, weil die Komprimierung von Dateien (z. B. durch GZip) effektiver ist und weil Browser (wie Chrome) einen größeren Overhead haben, der viele Ressourcen verarbeitet .

Inzwischen haben wir eine Datei als PHP-Objekt modelliert, wodurch wir dieses Objekt als Eingabe für andere Prozesse behandeln können. Insbesondere können wir denselben Vorgang oben wiederholen, um alle Dateien desselben Typs zusammenzufassen und die gebündelte Version anstelle aller unabhängigen Dateien bereitzustellen. Dazu erstellen wir eine Funktion get_content , die einfach den Inhalt aus jeder Ressource unter $fileObjects und erneut druckt, wodurch die Aggregation aller Inhalte aus allen Ressourcen erzeugt wird:

 abstract class SiteBundleBase extends SiteResourceBase { protected $fileObjects; function __construct($fileObjects) { $this->fileObjects = $fileObjects; } function get_content() { $content = ""; foreach ($this->fileObjects as $fileObject) { $content .= $fileObject->get_content().PHP_EOL; } return $content; } }

Wir können alle Dateien in der Datei bundled-styles.css indem wir eine Klasse für diese Datei erstellen:

 class StylesSiteBundle extends SiteBundleBase { function get_filename() { return "bundled-styles"; } function get_type() { return "css"; } }

Schließlich stellen wir diese gebündelten Dateien einfach wie zuvor in die Warteschlange ein, anstatt alle unabhängigen Ressourcen. Für CSS erstellen wir ein Bundle mit den Dateien header-colors.css , background-image.css und font-sizes.css , für das wir einfach StylesSiteBundle mit dem PHP-Objekt für jede dieser Dateien instanziieren (und ebenso können wir die JS Bundle-Datei):

 $fileObjects = array( // CSS new HeaderColorsSiteResource(), new BackgroundImageSiteResource(), new FontSizesSiteResource(), // JS new WelcomeUserDataUserResource(), new UserShoppingItemsUserResource() ); $cssFileObjects = array_map(array(ResourceUtils::class, 'is_css'), $fileObjects); $jsFileObjects = array_map(array(ResourceUtils::class, 'is_js'), $fileObjects); // Use this definition of $fileEnqueuer instead of the previous one $fileEnqueuer = new ResourceEnqueuer( array( new StylesSiteBundle($cssFileObjects), new ScriptsSiteBundle($jsFileObjects) ) );

Das ist es. Jetzt werden wir statt vieler nur noch eine JS-Datei und eine CSS-Datei anfordern.

Eine letzte Verbesserung der wahrgenommenen Leistung besteht darin, Assets zu priorisieren, indem das Laden von Assets verzögert wird, die nicht sofort benötigt werden. Lassen Sie uns das als nächstes angehen.

async / defer Attribute für JS-Ressourcen

Wir können Attribute async und defer zum <script> -Tag hinzufügen, um zu ändern, wann die JavaScript-Datei heruntergeladen, geparst und ausgeführt wird, um kritisches JavaScript zu priorisieren und alles Unkritische so spät wie möglich zu verschieben, wodurch das scheinbare Laden der Website verringert wird Zeit.

Um diese Funktion gemäß den SOLID-Prinzipien zu implementieren, sollten wir eine neue Schnittstelle JSResource (die von Resource erbt) erstellen, die die Funktionen is_async und is_defer . Dies würde jedoch die Tür zu <style> -Tags schließen, die diese Attribute möglicherweise auch unterstützen. Mit Blick auf die Anpassungsfähigkeit verfolgen wir also einen offeneren Ansatz: Wir fügen einfach eine generische Methode get_attributes zur Schnittstelle Resource hinzu, um flexibel zu bleiben, um jedes Attribut (entweder bereits vorhandene oder noch zu erfindende) für beide hinzuzufügen <script> - und <link> -Tags:

 interface Resource { // Continued from above... function get_attributes(); } abstract class ResourceBase implements Resource { // Continued from above... function get_attributes() { // By default, no extra attributes return ''; } }

WordPress bietet keine einfache Möglichkeit, den eingereihten Ressourcen zusätzliche Attribute hinzuzufügen, also machen wir es auf eine ziemlich hackige Art und Weise, indem wir einen Haken hinzufügen, der eine Zeichenfolge innerhalb des Tags durch die Funktion add_script_tag_attributes :

 class ResourceEnqueuerUtils { protected static tag_attributes = array(); static function add_tag_attributes($handle, $attributes) { self::tag_attributes[$handle] = $attributes; } static function add_script_tag_attributes($tag, $handle, $src) { if ($attributes = self::tag_attributes[$handle]) { $tag = str_replace( " src='${src}'>", " src='${src}' ".$attributes.">", $tag ); } return $tag; } } // Initize by connecting to the WordPress hook add_filter( 'script_loader_tag', array(ResourceEnqueuerUtils::class, 'add_script_tag_attributes'), PHP_INT_MAX, 3 );

Wir fügen die Attribute für eine Ressource beim Erstellen der entsprechenden Objektinstanz hinzu:

 abstract class ResourceBase implements Resource { // Continued from above... function __construct() { ResourceEnqueuerUtils::add_tag_attributes($this->get_filename(), $this->get_attributes()); } }

Wenn die Ressource welcomeuser-data.js nicht sofort ausgeführt werden muss, können wir sie schließlich auf defer setzen:

 class WelcomeUserDataUserResource extends UserResourceBase { // Continued from above... function get_attributes() { return "defer='defer'"; } }

Da es verzögert geladen wird, wird später ein Skript geladen, das den Zeitpunkt vorverlegt, an dem der Benutzer mit der Website interagieren kann. In Bezug auf Leistungssteigerungen sind wir jetzt bereit!

Es gibt noch ein Problem zu lösen, bevor wir uns entspannen können: Was passiert, wenn die Website auf mehreren Servern gehostet wird?

Umgang mit mehreren Servern hinter einem Load Balancer

Wenn unsere Site auf mehreren Sites hinter einem Load Balancer gehostet wird und eine von der Benutzerkonfiguration abhängige Datei neu generiert wird, muss der Server, der die Anfrage bearbeitet, irgendwie die neu generierte statische Datei auf alle anderen Server hochladen; Andernfalls werden die anderen Server von diesem Moment an eine veraltete Version dieser Datei bereitstellen. Wie machen wir das? Die Server miteinander kommunizieren zu lassen, ist nicht nur komplex, sondern kann sich letztendlich als undurchführbar erweisen: Was passiert, wenn die Website auf Hunderten von Servern aus verschiedenen Regionen läuft? Das ist natürlich keine Option.

Die Lösung, die ich mir ausgedacht habe, besteht darin, eine Indirektionsebene hinzuzufügen: Anstatt die statischen Dateien von der Site-URL anzufordern, werden sie von einem Speicherort in der Cloud angefordert, z. B. von einem AWS S3-Bucket. Nach der erneuten Generierung der Datei lädt der Server die neue Datei dann sofort auf S3 hoch und stellt sie von dort aus bereit. Die Implementierung dieser Lösung wird in meinem vorherigen Artikel Sharing Data Among Multiple Servers Through AWS S3 erläutert.

Fazit

In diesem Artikel haben wir berücksichtigt, dass das Inlining von JS- und CSS-Code nicht immer ideal ist, da der Code wiederholt an den Client gesendet werden muss, was die Leistung beeinträchtigen kann, wenn die Codemenge erheblich ist. Wir haben als Beispiel gesehen, wie WordPress 43kb Skripte lädt, um den Media Manager zu drucken, die reine JavaScript-Vorlagen sind und perfekt als statische Ressourcen geladen werden könnten.

Daher haben wir einen Weg gefunden, die Website schneller zu machen, indem wir den dynamischen JS- und CSS-Inline-Code in statische Ressourcen umwandeln, was das Caching auf mehreren Ebenen (im Client, Service Workers, CDN) verbessern kann und es ermöglicht, alle Dateien weiter zu bündeln in nur eine JS/CSS-Ressource, um das Verhältnis beim Komprimieren der Ausgabe zu verbessern (z. B. durch GZip) und um einen Overhead in Browsern durch die gleichzeitige Verarbeitung mehrerer Ressourcen zu vermeiden (z. B. in Chrome), und ermöglicht zusätzlich das Hinzufügen von Attributen async oder defer an das <script> -Tag, um die Benutzerinteraktivität zu beschleunigen und so die scheinbare Ladezeit der Website zu verbessern.

Als vorteilhafter Nebeneffekt ermöglicht die Aufteilung des Codes in statische Ressourcen auch, dass der Code besser lesbar ist, da Codeeinheiten anstelle von großen HTML-Blobs verarbeitet werden, was zu einer besseren Wartung des Projekts führen kann.

Die von uns entwickelte Lösung wurde in PHP erstellt und enthält einige spezifische Code-Bits für WordPress. Der Code selbst ist jedoch extrem einfach, kaum ein paar Schnittstellen definieren Eigenschaften und Objekte implementieren diese Eigenschaften nach den SOLID-Prinzipien und eine Funktion zum Speichern von a Datei auf Festplatte. Das wars so ziemlich. Das Endergebnis ist sauber und kompakt, lässt sich einfach für jede andere Sprache und Plattform neu erstellen und ist nicht schwer in ein bestehendes Projekt einzuführen – wodurch einfache Leistungssteigerungen erzielt werden.