Nicht jeder Programmierer denkt bei der Entwicklung eines Plugin (aber auch Themes) daran, ressourcenschonend mit den eingebunden Scripts & Styles umzugehen. Statt die Assets nur bei Seitenaufrufen zu laden, wo sie auch wirklich verwendet werden, werden häufig alle Datein bei jedem Seitenaufruf geladen. Das ist nicht nur schlecht für die Performance beim Leser sondern kostet auch einfach Resourcen.
Ein gutes Beispiel ist das beliebte Contact Form 7 Plugin (es gibt bessere Kontakformular-Plugins, ich bin z.B. ein Fan von HTML Forms, aber darum soll es hier nicht gehen). Installiert und aktiviert lädt es bei jedem Seitenaufruf 1,61 KB Stylesheet und 14,10 KB (+94,60 KB jQuery als Abhängigkeit) Javascript. Ein Kontaktformular haben die meisten Websites aber nur unter /kontakt/
eingebunden. Es ist also zu 99% unnötig, dafür Bandbreite und Rechenzeit zu verschwenden.
Viel geschickter wäre es, Style & Scripts nur zu laden, wenn bei einem Seitenaufruf auch der Shortcode für ein Formular [contact-form-7]
eingebunden ist.
Eigentlich lässt sich das ganz einfach in WordPress umsetzen:
- Im Plugin wird immer die Javascript- & CSS-Datei mit den zugehörigen Abhängigkeiten mit Hilfe der Funktion
wp_register_script
bzw.wp_register_style
registriert. Dadurch wird die Datei aber noch nicht auf der Seite eingebunden! - Wird auf der Seite wirklich z.B. der Shortcode ausgeführt, ruft man
wp_enqueue_script
bzw.wp_enqueue_style
mit dem selben Handle auf und die Datei wird in die Seiten eingebunden, vom Nutzer heruntergeladen und verwendet.
Nur wenn die enqueue
-Funktion aufgerufen wird, erfolgt auch die Einbindung in die Seite.
Leider wird aber sehr häufig von den Plugin-Autoren direkt und nur enqueue
-Funktion aufgerufen, weil sich damit die register
-Funktion übergehe lässt oder beide Funktionen werden direkt hintereinander aufgerufen.
Ein Ausweg aus dieser Hölle muss man sich dann selber bauen. Vor allem bei Plugins die Shortcodes (oder Blöcke verwenden), könnt ihr euch mit ein paar Zeilen Code in eurer functions.php
(am besten in eurem Child Theme) behelfen:
function cleanup_contact_form_7_dequeue() {
global $post;
if ( isset( $post->post_content ) && has_shortcode( $post->post_content, 'contact-form-7' ) ) {
return;
}
wp_dequeue_script( 'contact-form-7' );
wp_dequeue_style( 'contact-form-7' );
}
add_action( 'wp_enqueue_scripts', 'cleanup_contact_form_7_dequeue' );
Code-Sprache: PHP (php)
Dabei wird der post_content
mit der Funktion has_shortcode
darauf untersucht, ob er den Shortcode contact-form-7
enthält. Ist das der Fall, wird der Funktionsaufruf mit einem return;
beendet.
Gibt die Funktion has_shortcode
ein false zurück, wurde der Shortcode nicht im Content gefunden und die Funktion wird weiter ausgeführt.
In diesem Fall werden die für die Einbindung geplanten Scripts und Styles von mit der Funktion wp_dequeue_script wieder aus der Warteschlange entfernt und nicht eingebunden.
Hier noch einmal die Action verallgemeinert:
function cleanup_plugin_xyz_dequeue() {
global $post;
if ( isset( $post->post_content ) && has_shortcode( $post->post_content, 'xyz-shortcode' ) ) {
return;
}
wp_dequeue_script( 'plugin-xyz-script-handle' );
wp_dequeue_style( 'plugin-xyz-style-handle' );
}
add_action( 'wp_enqueue_scripts', 'cleanup_plugin_xyz_dequeue' );
Code-Sprache: PHP (php)
Tipp: Falls die dequeue Funktion keinen Effekt habt, checkt ob ihr den richtigen Handler verwendet. Ansonsten könnt ihr versuchen die Priorität eurer Action zu erhöhen indem ihr add_action
einen dritten Parameter mit der Priority hinzufügt z.B. 20 (Standard ist 10): add_action( 'wp_enqueue_scripts', 'cleanup_plugin_xyz_dequeue', 20 );
Folgende Punkte müsst ihr ändern:
- Funktionsname:
cleanup_plugin_xyz_dequeue
müsst ihr inadd_action
und als Funktionsnamefunction
ändern und muss eindeutig und einmalig sein. - Shortcode: Der registrierte Tag des Shortcodes
xyz-shortcode
inhas_shortcode
- Handler: Jeweils für Script und Style müsst ihr den ursprünglich verwendeten Handler herausfinden und in die
wp_dequeue_script
bzw.wp_dequeue_style
Funktion einsetzen
Blocks
Statt has_shortcode
für Shortcodes kann auch has_block
für Gutenberg Blöcke verwendet werden:
function cleanup_block_xy_dequeue() {
if ( has_block( 'block_type' ) ) {
return;
}
wp_dequeue_script( 'my_script_handle' );
wp_dequeue_style( 'my_style_handle' );
}
add_action( 'wp_enqueue_scripts', 'cleanup_block_xy_dequeue' );
Code-Sprache: PHP (php)
Damit sieht die Funktion auch deutlich übersichtlicher aus, weil nicht das globale $post
Objekt verwendet werden muss.
Handler herausfinden
Das ganze funktioniert natürlich bei vielen Plugins. Allerdings muss man immer den verwendeten Handler für das jeweilige Script oder Style herausfinden. Das geht am besten wenn ihr innerhalb eurere Funktion folgende Arrays euch ausgeben lässt:
print_r(wp_scripts()->queue);
print_r(wp_styles()->queue);
Code-Sprache: PHP (php)
Die Ausgabe sieht dann z.B. wie folgt aus:
Array ( [0] => admin-bar [1] => contact-form-7 )
Array ( [0] => admin-bar [1] => wp-block-library [2] => contact-form-7 )
Code-Sprache: PHP (php)
In diesem Fall ist wird der Handler contact-form-7
jeweils einmal für das Script und einmal für den Style verwendet. Leider gibt es aber kein Standard für die Benennung des Handlers, deswegen bringt Raten leider meistens nicht den gewünschten Erfolg.
Fazit
Die vielzahl an Plugins und Themes machen WordPress extrem mächtig aber leider ist nicht jedes Theme/Plugin gut programmiert oder wird seit Jahren nicht mehr gewartet. Will man nicht direkt ein Fork, was dank GPL ja möglich ist, pflegen, muss man sich mit solchen Tricks behelfen.
Siehst du einen Nachteil darin, das Ganze nach
template_redirect
vorzuverlagern und die Funktion, die Skripte/Styles registriert/enqueued, gleich ganz auswp_enqueue_scripts
zu entfernen?Also für CF7 z.B.:
Gerade mal getestet: Funktioniert ebenfalls (wenn man
if ( ! has_shortcode(...
verwendet, ich habe es bei deinem Kommentar angepasst). Allerdings muss man dann im Sourcecode des Plugins nach dem entsprechenden Funktionsnamen suchen. Den Handler herauszufinden ist glaube ich einfacher. Auf jeden Fall aber auch eine gute Lösung an die ich bisher nicht gedacht habe. Ist wahrscheinlich ein bisschen performanter.Speziell für Contact Form 7 gibt es auch die Möglichkeit mit zwei Filtern zu arbeiten: https://gist.github.com/cyberwani/38f945007e83600ca0bf0bc48d99361b Ich wollte es aber allgemeiner halten und habe deswegen auf die
wp_enqueue_scripts
Action gesetzt.Stimmt, macht Sinn.
Das habe ich mich tatsächlich auch gefragt, war aber zu faul/müde, es richtig zu testen; und in der Praxis wäre der Unterschied (wenn es denn einen gibt) sicher eh nicht relevant.
Schön erklärt, btw! ?
Danke 🙂
Perfekt! Danke für die Inspiration. Habe ich gerade genutzt, um unnütze Google-Fonts eines Plugins zu entfernen.
Cool!
Danke für diesen Beitrag, der fasst das Thema gut zusammen!
Eine Sache ist mir beim Lesen aufgefallen: du schreibst, dass der Standard-Wert der Priorität für die Actions 20 ist. Ich kenne eigentlich 10 als Standard-Wert – hab ich da was verpasst? 🙂
Da hast du natürlich recht. Direkt angepasst.
Hey!
CF7 hat zu dem Thema auch selber einen Artikel in englischer Sprache:
https://contactform7.com/loading-javascript-and-stylesheet-only-when-it-is-necessary/
Ein Fix nur für sein eigenes Projekt ist immer gut. Mehr Leuten würde es helfen wenn das Plugin/Theme selber angepasst wird und dann für alle Websites eine Verbesserung entsteht.
Hast du mal versucht Issues bei den Herstellern selber aufzumachen? Die aktuelle Debatte sollte dem Wunsch etwas Aufmerksamkeit sichern.
Es grüßt
derRALF
Contact Form 7 hat mir hier hauptsächlich als Beispiel gedient. Selber setzte ich es nur bei einem Projekt ein was ich zwar betreue aber dort nicht austauschen darf „Never change a running system“. Leider gibt es für Contact Form 7 keine offizielle Github Repository sonst hätte ich da wohl mal ein Pull Request erstellt.
Ich glaube die Plugin Autoren haben einfach zu viel Angst daran was zu ändern, weil sie ggf. viele Seiten zerschießen. Vor allem bei Formular Plugins ist mir in der Vergangenheit mehrfach aufgefallen, dass sie Styles & Scripts überall laden. Vermutlich weil die Nutzer das Formular nicht nur in Beiträgen/Seiten einbinden sondern an allen möglichen Stellen.
Danke für den Artikel.
Meine Frage aus Sicht eines NUR-Anwenders:
Funktioniert das auch, wenn durch ein Plugin (z.B. Fast Velocity Minify) css-Minimierung erfolgt?
Hallo Wolfgang,
ja das sollte auch mit einem Plugin wie Fast Velocity Minify funktionieren.
Danke Johannes; ich hab’s ausprobiert und es klappt gut. Wichtig ist nur, den Cache zu leeren. Ich hatte mich erst gewundert, dass kein Effekt zu sehen war. 😉
Übrigens:
Bei der Suche nach weiteren Ressourcenfressern bin ich auf die Funktion COVERAGE in Chrome gestoßen. Das Ergebnis war für mich ein leichter Schock. Von insgesamt 2,3MB meiner Startseite verbrauchen die „Top Ten“ der geladenen js-Dateien 1,3MB „unused Bytes“. Und diese Scripts liegen ausschließlich im Pfad /wp-includes/js/dist/. Da kannst du scripts minimieren und kombinieren – den Müll wirst du dadurch auch nicht los. Bei allen nötigen Bemühungen, performante und effektive Themes zu erstellen – aus meiner Sicht liegt ein großes Problem im Core.
Oder sehe ich das etwas falsch?
2,3mb Scripts ist schon sehr sehr viel. Die Scripts aus /wp-includes/ werden i.d.R. nur geladen, wenn sie von Plugins oder dem Theme angefordert werden. Am besten mal alle Plugins deaktivieren die du nicht unbedingt benötigst und schauen ob es dann besser ist.
Jaaaa,
nach langem Suchen und mit schrittweisem De- und Aktivieren aller Plugins habe ich den Verursacher gefunden: „Event Post“ von N.O.U.S. allein verwendet über 700kB JS und verursacht zusätzlich fast 1,4MB ungenutzte JS-Aufrufe. Es ist zwar komfortabel, aber dieser Verbrauch ist für mich ein ko-Kriterium.
Mich hat der Pfad in die Irre geführt.
Danke für den Tip
Gerne, sehe du hast auch schon die Plugin-Autor benachricht. Sehr gut 😉
Hallo,
ich habe da noch ein weiteres Plugin, was zuviel Sachen lädt, wenn diese eigentlich nicht benötigt werden. Es geht um Commons Booking (https://de.wordpress.org/plugins/commons-booking/).
Ich verwende es auf https://www.diabetes-hilfe-nuernberg.de/ für eine interne Bibliothek. Wenn ich Seiten mit den Shortcodes habe, dann kann ich den Ballast los werden. Was mache ich aber, wenn ein CPT des Plugins in der Form https://www.diabetes-hilfe-nuernberg.de/cb-items/kinder-und-jugendliche-mit-diabetes/ ausgegeben wird? Hier gibt es keinen Shortcode den ich abfragen könnte.
Wer kann mir da helfen?
Gruß Frank
Hallo Frank,
Leider auch hier wird direkt die enqueue-Funktion verwendet: https://github.com/wielebenwir/commons-booking/blob/master/public/class-commons-booking.php#L624 Interessanterweise aber nur für Styles, für ein paar Scripts wird mit
is_singular ( 'cb_items' )
der CPT gechekt: https://github.com/wielebenwir/commons-booking/blob/master/public/class-commons-booking.php#L664Genau das Gleiche können wir für die Styles nachrüsten. Versuch mal diese Funktion (nicht selber getestet)
Ich hoffe das hilft dir.
Hallo Johannes,
vielen Dank für die Info. Ich konnte mir in der Zwischenzeit schon helfen und habe folgende Lösung (für alle Plugins):
Ich entferne erstmal alle Scripte und Styles und lade diese erst wieder, wenn diese wirklich benötigt werden (Abfrage mittels has_block oder has_shortcode oder is_singular oder is_page).
Es schein mir wohl eine Art Plugin-Krankheit zu sein, einfach alles zu laden, egal ob benötigt oder nicht. Ich habe auf der Website (https://www.diabetes-hilfe-nuernberg.de/) neben Commons Booking noch andere aktiv, wo ich dies so mache.
Meine Idee: Ein eigenes Plugin dafür zu schreiben, wo man im Backend es sauber und schön steuern kann. Johannes, hast Du Lust mich dabei zu unterstützen? Es wäre mein erstes Plugin…
Gruß Frank
Hallo Frank,
im Idealfall sollte man eher die Plugin-Autoren darauf hinweisen wie es korrekt gemacht wird. Dann würde man sich so ein Plugin sparen.
Die Frage, die sich mir stellt, ist, ob man das für den Endnutzer sinnvoll umsetzen kann. Kann man dem Endnutzer zutrauen die korrekt ID von einem Block oder Custom Post Type herauszufinden? Wenn nicht ist die Frustration schnell sehr groß. Das sehe ich als das größte Problem für so ein Plugin.
Ich gebe gerne mein Senf zu so ein Plugin und helfe dir gerne mit Rat und Tat aber bevorzugen würde ich den Weg das sinnvolle enquenen von Script/Styles in die Plugin Guidelines aufgenommen und auch vorausgesetzt wird. Vielleicht muss man dazu mal eine Diskussion starten.
Dieser Artikel wurde erwähnt auf gist.github.com
Hi Johannes,
ich schlagemich mit dieser Problematik auch gerade rum. Ich habe eine Onepage in WP, auf der ich ein per PHP-Code selbst erstelltes Formular habe, mit dem zwei Datumswerte an eine Reservierungsseite übergeben werden, die mit cf7 erstellt ist. Die beiden Datepicker-Felder auf der Hauptseite benutzen allerdings das jquery-ui.min.css von cf7 und nicht das vom datepicker. Vielleicht hast du eine Idee? Mit deiner Lösung habe ich keinen Erfolg gehabt.
Andreas
Willst du das jquery-ui.min.css entfernen oder wird es nicht auf der richtigen Seite geladen?
Dieser Article wurde erwähnt auf wpletter.de
Dieser Article wurde erwähnt auf wpletter.de