PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Externe Javascript Dateien zusammenfassen

with 30 comments

Wir alle wissen: Langsame Webseiten bekommen weniger Besucher. Dazu gibt es auch Auswertungen von großen Portalen und Suchmaschinen.

Doch wie beschleunigt man die eigene Webseite? Häufig übersieht man einen wichtigen Teil: Das Frontend bzw. den Client.

Mag das PHP-Script auch sehr schnell sein (die Zeit gemessen mit microtime() ergibt 0.1 Sekunden), die Seite kann sich trotzdem langsam anfühlen. Das liegt häufig an vielen externen Dateien, die der Browser noch nachladen muss. Dazu gehören sowohl CSS-Dateien, Javascript-Dateien, Bilder usw.

Nachdem der Browser den kompletten HTML-Inhalt empfangen hat, fängt er an, ihn zu parsen. Falls er dabei auf externe Dateien stößt, lädt er sie nach. Nehmen wir mal folgendes Beispiel:

<html>
<head>
	<title>Testpage 1</title>

	<link rel="stylesheet" type="text/css" href="css/style.css">
	<link rel="stylesheet" type="text/css" href="css/custom.css">
	<link rel="stylesheet" type="text/css" href="css/calendar.css" media="screen">

	<script type="text/javascript" src="js/custom/helpLayer.js"></script>
	<script type="text/javascript" src="js/custom/filterRewriteUrl.js"></script>
	<script type="text/javascript" src="js/custom/other.js"></script>
	<script type="text/javascript" src="js/firebug_for_ie/firebug-lite-compressed.js"></script>
</head>
<body>
	<script type="text/javascript" src="js/wz_tooltip/wz_tooltip.js"></script>
	Just a testpage
</body>
</html>

Es werden also 3 css-Dateien und 5 js-Dateien nachgeladen. Grafisch dargestellt (mit dem Firefox Addon Firebug) sieht das dann so aus (man beachte: das ist localhost, durchs Internet wären die Verzögerungen noch etwas größer bei jedem Request):

firebug1a

Man kann schön erkennen, dass die css-Dateien parallel bearbeitet werden. Die Javascript-Dateien werden nacheinander geladen, da es ja Abhängigkeiten geben könnte und die Reihenfolge wichtig ist.

Nun kann man natürlich erstmal die unnötigen Dateien entfernen. Da ich das Tooltip-Script nicht brauche (und es sowieso unschön ist, es im body aufzurufen, aber das muss so bei diesem Script), entferne ich es, und erhalte dann:

firebug2

Wir haben also 31KB gespart und auch 15% der Ladezeit.

Nun fassen wir die css-Dateien und js-Dateien zusammen mittels PHP, das sieht dann so aus:

<html>
<head>
	<title>Testpage 3</title>

	<link rel="stylesheet" type="text/css" href="css/css.php">

	<script type="text/javascript" src="js/js.php"></script>
</head>
<body>
	Just a testpage
</body>
</html>
<?php
header('Content-type: text/javascript');

$output  = file_get_contents('custom/helpLayer.js');
$output .= file_get_contents('custom/filterRewriteUrl.js');
$output .= file_get_contents('custom/other.js');
$output .= file_get_contents('firebug_for_ie/firebug-lite-compressed.js');

// remove comments from JS (this can cause problems if brackets
// or semikolons are not used correctly)
#$output = preg_replace('#//.*#', '', $output);
#$output = preg_replace('#\n|\r\n|\r|\t#', '', $output);

echo $output;
<?php
header("Content-Type: text/css");

$output =  file_get_contents('css/style.css');
$output .= file_get_contents('css/custom.css');

echo $output;

Man sieht auf den ersten Blick: Wir haben nur noch 2 externe Dateien eingebunden. Wie sieht die Ladezeit aus?

firebug4

Wir haben also, ohne irgendeine Funktionalität zu ändern oder js-Dateien manuell zusammenfassen zu müssen die Hälfte der Zeit gespart (von 407ms auf 190ms). Ein PHP-Script von 400ms auf 200ms zu beschleunigen ist mehr Arbeit glaube ich ;-)

Was lernen wir daraus? So wenig externe Dateien wie möglich laden oder sie zusammenfassen, um die Anzahl der HTTP-Request zu vermindern. Es gibt noch weitere Tricks, die ich aber noch nie produktiv eingesetzt habe. In hoch produktiven Webseiten kann das aber vielleicht Sinn machen, hier eine SEHR GUTE Präsentation zu dem ganzen Thema, die man sich angucken MUSS als Webentwickler:

Written by Michael Kliewe

August 25th, 2009 at 7:59 pm

Posted in PHP

Tagged with , , ,

30 Responses to 'Externe Javascript Dateien zusammenfassen'

Subscribe to comments with RSS or TrackBack to 'Externe Javascript Dateien zusammenfassen'.

  1. Mir gefällt da noch besser, die JS/CSS-Dateien bereits bei der Auslieferung der eigentlichen Seite in einem assets-Verzeichnis zusammengefasst zu veröffentlichen. So kann der Apache als Auslieferer genutzt werden, es wird kein weiterer Request an das PHP abgesetzt, ich kann das Deflate-Module nutzen, …
    Ich habe da mal etwas sehr nettes zusammengeschrieben, auch mit JSMin etc., das ist allerdings in einem geschlossenem Projekt im Einsatz.

    Martin Kuckert

    25 Aug 09 at 20:45

  2. Würde mich mal interessieren wie das geht, hab ich noch nie von gehört…

    Wobei man ja auch leider nicht immer einen Apache zur Verfügung hat, in der Firma nutzen wir zu 95% IIS (ärgert mich auch, aber ist halt so wegen Verträgen und dem Kram). Privat zwar Apache, aber da hab ich selten mehr als 1 JS und 1 CSS ;)

    Michael Kliewe

    25 Aug 09 at 21:51

  3. Wenn ich so viel Zeit wie Lust hätte, dann wäre der Artikel zu Minify (http://code.google.com/p/minify/) schon lange in meinem Blog gelandet. :)
    Minify packt nicht nur CSS und Javascript, sondern auch HTML (wenn auch nur minimal). Dabei kann man sogar verschiedene Gruppen zusammenfassen und das ganze Cachen lassen. Ich nutze das Skript schon in mehreren Projekten.

    Leider ist die Zeit gerade dermaßen knapp bei mir, dass es einfach nicht passt. Vielleicht hast du ja Lust einen Artikel über Minify zu schreiben?

    Roman

    25 Aug 09 at 22:52

  4. Du könntest fürs Deployment z.B. Ant oder Phing nutzen um solche Tasks zu übernehmen. Also z.B. Dateien aus SVN exportieren, Unit Tests laufen lassen, JS und CSS zusammenfassen, Dateien auf Server spielen.

    Phil

    25 Aug 09 at 23:32

  5. Warum wurde eigentlich das CSS zusammengefasst, wenn der Browser es doch schafft diese gleichzeitig zu laden? Für mich sieht es so aus, als würde die CSS Dateien zusammengefasst mehr Zeit benötigen.
    Außerdem liefert “css.php” 2KB aber die 3 CSS Dateien hatten 9,8 und 1 KB … blick ich da was nicht? Doch irgend ein Komprimierungstool verwendet? Oder zählt der HTTP Header hier auch zur Größe dazu?
    Und warum wurden nur 2 Dateien zusammengefasst und nicht alle 3?
    .. Fragen über Fragen ;)

    Rudwig Brown

    26 Aug 09 at 08:08

  6. Das Minify-Projekt kannte ich noch nicht. Aber sieht sehr ähnlich aus, wie die eigene Entwicklung, allerdings etwas weniger “statisch”.
    @Michael: Das guck dir beispielhaft mal an ;-) Hat übrigens wenig mit dem Webserver zu tun. Das Deflate-Module verwendet vor der Datenauslieferung eine gzip-Kompression. Das Expires-Modul fügt entsprechende Header hinzu. Also, im Grunde meine ich damit, dass ich solche Auslieferungen von statischen Dateien den Webserver überlassen würde.

    Martin Kuckert

    26 Aug 09 at 09:14

  7. Hallo Michael,

    die Methode um Daten zu komprimieren, die du hier beschreibst, ist nicht so gut wie sie ausschaut!

    Problemfalle ist hierbei der Browser. Jeder Browser cacht gewisse JS- und CSS-Files. Dadurch, dass du die JS- und CSS-Dateien als PHP-File auslieferst (zumindest wird dem Browser das vorgetäuscht) wird dieser nie die Files cachen (können).

    Das heißt die Datenmengen, welche du durch den 1.Request spart, wirst du durch die folgenden Seitenrequest des gleichen Users wieder einbüßen, da ja jedes Mal die JS- und CSS-Files angefragt werden.

    Ansonsten ist die Präsentation & YSlow super! HTTP-Request zu verringern, ist sicherlich wichtig, aber viel wichtiger ist es, den Browser-Cache sinnvoll & sinnig auszunutzen und da wurde das Expire-Modul vom Apache ja schon genannt (gibt sicherlich auch äquivalentes für den IIS).

    Viele Grüße
    Ulf

    Ulf

    26 Aug 09 at 12:50

  8. Ich bin mittlerweile auch davon überzeugt, dass die Lösung mit Ant/Phing, also einmaliger Generierung, und dann Auslieferung mittels Webserver (ohne PHP) die optimalste Lösung ist.

    Eigentlich wollte ich auch nur Javascript behandeln (siehe Titel), das mit dem CSS ist dann nur noch dazwischengerutscht. Im Grunde ging es mir um die Anzahl der HTTP-Requests, die es zu verringern gilt, und was das ausmachen kann, gerade bei vielen Javascript-Dateien, die sequenziell geladen werden.

    Minify kannte ich noch nicht, habe vor Jahren das Thema schonmal auf dem Tisch gehabt. Die Namen hier sind mit bekannt gewesen (da gibts auch tolle Vergleichstabellen):
    http://compressorrater.thruhere.net/

    Michael Kliewe

    26 Aug 09 at 13:35

  9. Phing ist ein Ansatz für das Deployment. Verwende ich für einige Projekte auch. Der dynamische Ansatz ist aber sehr elegant, wenn das System über einen PlugIn-Mechanismus verfügt und die PlugIns eigene JavaScripts registrieren können. Alle Skripte sammeln, minifizieren und wegschreiben. Ist die Datei noch aktuell vorhanden, natürlich nix weiter tun. Hat den Charm eines einzelnen HTTP-Requests und die Einstellungen vom Webserver für die Auslieferung greifen direkt. Man muss natürlich drauf achten, die Dateien vernünftig zu mergen, d.h. eindeutiger Name und Zeitstempel mit an die URI anhängen, um keinen Müll in den Browsern zu behalten.
    Habe mir Minify jetzt nicht ganz genau angesehen, aber denke, das wird ähnlich arbeiten.

    Martin Kuckert

    26 Aug 09 at 14:31

  10. Ich finde auch, dass sich das bei CSS nicht wirklich lohnt. Was zusammenfassen von JS Files anbetrifft, ist Magento ein Beispiel. Besucht mal die Demo (http://demo.magentocommerce.com/) und schaut euch den Quelltext an.

    Rudwig Brown

    26 Aug 09 at 19:20

  11. Ich denke das eintscheidende, was viele schon nicht wissen, ist überhaupt verschiedene CSS-Dateien direkt nacheinander zu laden, da diese parallel geladen werden. Ich denke nicht, dass es Sinn macht diese zusammenzufassen, da man kaum eine Einsparung erhalten wird.

    Mirco

    28 Aug 09 at 12:04

  12. […] einem meiner letzten Artikel zum Thema “ Externe Javascript Dateien zusammenfassen” haben wir bereits gemerkt, dass man HTTP-Requests verringern sollte, um die Performance auf […]

  13. Hallo,
    ich wollte meinen Blog mit Hilfe von zusammengefassten .js Dateien optimieren und bin auf dein Tut gestossen. Hat mir sehr gut gefallen. Habe es auch umgesetzt aber die Scripts werden nicht ausgeführt. Habe nicht so die Ahnung.
    Hast du vielleicht eine Idee, warum nicht?
    Meine Code.

    Wenn ich im Sourcecode auf den Link klicke, werden mir alle Scripts angezeigt. Sie werden aber nicht ausgeführt. Wenn ich die Scripts direkt wieder einbauen, funktionieren sie.

    LG
    astera

    astera

    17 Nov 09 at 18:30

  14. Hi,

    “aber die Scripts werden nicht ausgeführt” Was genau meinst du damit? Werden sie geladen? Gibt es Javascript-Fehler?

    Installier dir am besten Firebug und aktiviere die Module (Rechtsklick auf das Käfersymbol rechts unten -> Alle Module aktivieren), dann siehst du im Netzwerk-Tab, ob die Javascript-Dateien geladen werden. Im Skript-Tab siehst du den Javascript-Code, und in der Konsole eventuelle Javascript-Fehler.

    Vielleicht liegt es ja daran, dass das PHP-Script, das die JS-Dateien zusammenfasst, die Umbrüche entfernt. Falls in deinen Javascript-Dateien zum Beispiel keine Semikolons am Ende der Zeilen sind, wird der Javascript-Code dadurch fehlerhaft.

    Hoffe das hilft.

    Michael Kliewe

    17 Nov 09 at 19:36

  15. Hallo Michael,

    Danke, für deine Antwort.
    Vscript wird ausgeführt.
    Die Console schreibt:
    missing ; before statement
    [Break on this error] <!–\n

    Die Zusammenfassung der Scripts sieht so aus:

    window.google_analytics_uacct = “UA-1362045-4″;

    var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.” : “http://www.”);
    document.write(unescape(“%3Cscript src='” + gaJsHost + “google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E”));

    try {
    var pageTracker = _gat._getTracker(“UA-1362045-4″);
    pageTracker._trackPageview();
    } catch(err) {}
    Ich sehe nicht, wo ein Semikolon fehlen sollte.
    Hast du eine Idee?
    Danke
    astera

    astera

    17 Nov 09 at 20:46

  16. Sorry,
    die Ausgabe ist:

    window.google_analytics_uacct = “UA-1362045-4″;

    var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.” : “http://www.”);
    document.write(unescape(“%3Cscript src='” + gaJsHost + “google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E”));

    try {
    var pageTracker = _gat._getTracker(“UA-1362045-4″);
    pageTracker._trackPageview();
    } catch(err) {}

    astera

    17 Nov 09 at 21:01

  17. Leuchtet mir irgendwie nicht ein. Das ist doch nur der Google Analytics Javascript Code. Mehr Javascript Code hast du nicht? Wofür brauchtest du dann mein Script zum Zusammenfügen von mehreren Javascript-Dateien?

    Liefert das PHP-Script nur diese 7 Zeilen zurück? Du kannst sonst auch die URL des PHP-Scriptes mal oben im Browser angeben und gucken, was dabei rauskommt.

    Die Fehlermeldung deutet aber irgendwie schon darauf hin, dass Semikolons fehlen am Zeilenende, und dass das Zusammenfassen deshalb Fehler produziert.

    Sollte es daran liegen, mußt du einfach nur die Zeilen 11 und 12 in meinem Script auskommentieren, dann werden Kommentare nicht entfernt, und auch Zeilenumbrüche bleiben vorhanden. Dann sollte der Javascript-Code nicht kaputt gehen beim Zusammenfassen.

    Michael Kliewe

    18 Nov 09 at 00:51

  18. Hallo,

    ich habe folgendes gemacht. In meiner vscript.php:


    Und dann ersteinmal 4 scripts eingebunden.


    und die zweite:

    tfb.account="autos_online";tfb.label="follow-us";tfb.color="#94CC3D";tfb.side="r";tfb.top=260;tfb.showbadge();

    usw…
    Es klappt aber nicht.

    LG
    astera

    astera

    19 Nov 09 at 18:51

  19. Sorry ich nochmal.
    Wie kann ich dir hier den php code posten?

    astera

    19 Nov 09 at 18:52

  20. http://javascriptcompressor.com/
    Wäre für das Komprimieren von js eine Alternative.
    Allerdings natürlich nur sinnvoll, wenn sich der Code nicht oft ändert ;)

    maTTes

    26 Dez 09 at 10:47

  21. Offtopic: Habt ihr eigentlich schon gewusst das man kein Semikolon am Ende einer Zeile in Javascript machen muss?

    daFoxy

    15 Jan 10 at 09:52

  22. Ja, das kann ein Problem sein beim zusammenführen/verkleinern/obfuscaten, wie bereits im Kommentar am 17.09. angemerkt.

    Michael Kliewe

    15 Jan 10 at 10:53

  23. […] man die größten Performanceverbesserungen auf der Browserseite erreichen. Ich habe bereits das Zusammenfassen von CSS- und Javascript-Dateien vorgestellt, ebenso wie das Zusammenfassen von Bildern in CSS-Sprites. Damit wird die Anzahl der […]

  24. […] Anzahl der Request vermindern durch Zusammenführung aller Javascripts, CSS etc. in jeweils eine einzige Datei (weitere Beispiele hier bei phpperformance oder hier zu […]

  25. Hi,

    bin grad hier draufgestoßen und auch auf die Seite von minify. Ich würde sogar noch einen Schritt weitergehen, ich weiß nur nicht obs eher ein nach- oder ein vorteil oder eher gleich zu betrachten ist.

    Ich setz auf Netbeans und auf die Ant-Variante von Google Closure mit dem Attribut “Advance” für eine super Komprimierung, wie ich finde.
    Google Closure kann natürlich auch Javascript-Dateien kombinieren. Der Nachteil, es kann NUR js-Dateien und keine CSS. Für CSS benutze ich YUI Compressor.

    So nun habe ich einen Ordner voll mit minimierten CSS und JS-Dateien. Nun würde ich ein PHP Script erstellen welches die Dateien jeweils in eine Funktion packt die den ganzen minimierten Output zurückgibt (als JSON Objekt, damit ich am Ende erkennen kann ah dass ist CSS und das ist JS).

    Nun habe ich eine Index.html und nur eine Javascript-Datei die eingebunden wird, welche nämlich ein AJAX Request zu genau dieser PHP Datei ausführt, jeweils ein und ein tag erstellt und diese strings (die minimierten JS Daten und CSS Daten) direkt in die Seite, die Tags schreibt.

    Warum ich das so Trenne? Ich bin kein Freund von Inline-Code oder wie es ASP.NET-Leute nennen, Fluent HTML. Für mich hat PHP NICHTS in einer HTML Seite zu suchen, dafür gibt es Template engines und genauso sollte es sein und bleiben.

    Bin gespannt auf euer Feedback, würde mich freuen.

    Gruß
    Chris

    Chris

    8 Jun 11 at 15:08

  26. Besser ist die Dateien schon zusammen gebaut auf dem Server belassen. Pro Client Initial Anfrage immer wieder zusammenbauen macht kein Sinn. Wenn die Dateien beim deployen Komprimiert werden dann kann man diese auch gleich zu einer Datei zusammenbauen. Dann über das HTML Script Element eine Datei für z.B. das Framework einbinden und alle anderen JavaScript Klassen dynamisch Nachladen per onload bzw. onreadystatechange und die Klassen dynamisch ausführen. Nach dem Vorgehen wie in PHP autoloader, singeltone, frontcontroller dispatcher filter usw. und dies in JavaScript

    ECMA-262 3rd

    11 Jul 11 at 12:40

  27. Danke für die tolle Zusammenfassung, leider scheine ich irgendwas falsch zu machen. Ich hab mich erst mal nur an die Zusammenfassung meiner CSS Dateien versucht. Wenn ich das wie beschrieben mache, wird mir am Ende von “echo $output;” der Inhalt aller CSS Dateien direkt im Browser ausgegeben.
    Das Layout der Seite selbst ist dann natürlich auch völlig hinüber… was mache ich falsch?

    Thomas

    15 Aug 12 at 23:06

  28. @Thomas: Das ist prinzipiell auch richtig. In meinem Beispiel oben wird die PHP-Datei, die das CSS ausgibt, so eingebunden in die HTML-Seite:
    <link rel=”stylesheet” type=”text/css” href=”css/css.php”>
    Du kannst auch alternativ den komprimierten CSS-Code direkt in die HTML-Seite einbetten indem du das CSS zwischen den Tags <script type=”text/css>….</script> ausgibst.

    Das CSS wird also nach wie vor normal eingebunden (extern oder inline), nur eben komprimiert.

    Michael Kliewe

    20 Aug 12 at 11:44

  29. Hi Michael,

    danke für deine Antwort! Was ich meinte war, dass alle CSS Regeln plötzlich als Plain Text im Browser ausgegeben wird, was natürlich total für die Tonne ist. :)

    Thomas

    4 Sep 12 at 11:29

  30. Hallo zusammen,
    was man noch hinzufügen könnte wäre,

    header(‘Cache-Control: no-cache, must-revalidate’);
    header(‘Expires: Sat, 26 Jul 1997 05:00:00 GMT’);
    header(‘Content-type: text/javascript; charset=utf-8′);

    speziell das charset=utf-8 ist halt wichtig wenn die scripte alle utf-8 konform sind. Ebenso kann man natürlich auch mit dem Cache und den Expires rumspielen wenn man die Scripte oder Css files gerne länger im Browser Cache halten möchte.

    LG
    Sebastian

    Sebastian

    22 Apr 13 at 19:10

Leave a Reply

You can add images to your comment by clicking here.