Łącza internetowe są coraz szybsze, co wcale nie oznacza, że można tworzyć bardzo "ciężkie" strony www. Zawsze należy mieć na względzie rozmiar plików, które internauta będzie musiał od nas pobrać. W tym artykule pokażę, jak zmiejszyć rozmiar plików JS oraz ograniczyć liczbę żądań HTTP wysyłanych przez przeglądarkę do serwera.
Pobierz cały kod
SVN
Prosty Kod
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Minimalizacja JS</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<script src="/js/file_0.js" type="text/javascript"></script>
<script src="/js/file_1.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
Oto kod pliku `js/file_0.js':
/**
* Kolejny przyklad pliku z kodem JS
* */
var a = {
'aaa' : "sadasdasasdasd", // to jest komentarz
'bbb' : 12,
'ccc' : {
'aa' : 123
},
'ddd' : [
1, // Tu jeszcze wiecej komentarza
2,
3,
4,
5
]
};
Oraz `js/file_1.js':
/**
* Funkcja przykladowa, nic nie robi procz tego, za zajmuje
* troche miejsca
*
* @param b parametr 1
* @param c parametr 2
* @param d parametr 3
* */
function a(b, c, d)
{
b = 10; // to jest komentarz
// tu jeszcze wiecej komentarza
return ( b + c ) * d;
}
Na stronie tej nic się nie wyświetli. Używając Firebuga można podejrzeć co i jak długo się ładuje:
Jak widać na powyższym obrazku. Mamy 3 requesty: 1 całej strony, oraz dwa dla plików JS. każdy z tych plików jest wyjątkowo mały, ale wymaga osobnego żadania de serwera, serwer się chwile "zastanowi" po czym zwróci jakiś wynik. Zajmuje to sporo czasu. A można czas ten skrócić...
Załączanie jednego pliku
Na początek spróbujemy ograniczyć liczbę żądań HTTP wysyłanych do serwera. W sekcji head naszej strony wprowadźmy takie zmiany::
<head>
<title>Minimalizacja JS</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<script src="/javascript.php" type="text/javascript"></script>
</head>
na razie wstawmy cokolwiek do pliku `javascript.php' tylko po to, aby zobaczyć czy ładuje się prawidłowo:
<?php
echo 'alert("działa")';
jak widzimy, trochę przyspieszyło (choć ktoś może powiedzieć, że to zasługa mniejszego pliku).
No dobra, ale przecież nie mamy naszego kodu :( Spokojnie, już go wstawiamy. Ręcznie? No, jeśli nie masz co robić z własnych życiem, to możesz ręcznie. Ja jednak wolę to zautomatyzować :)
Minimalizacja kodu JS
Do minimalizacji kodu wykorzystam klasę PHP napisaną przez Douga Crockforda.
Zmieńmy kod pliku `javascript.php':
<?php
require_once 'jsmin.class.php'; // zakladam, ze pobrales plik z klasa i jest on dostepny w tej sciezce
echo JSMin::minify(file_get_contents('js/file_0.js'));
echo JSMin::minify(file_get_contents('js/file_1.js'));
Odpalmy teraz naszą stronę. Po takich zabiegach otrzymujemy:
Jak widać, pliki zostały scalone, komentarze usunięte i całość działa nieznacznie szybciej. Przy większej liczbie plików zysk byłby lepiej widoczny.
Co jednak nadal jest złe? Za każdym razem, gdy ktoś wchodzi na stronę tworzony jest ten plik od nowa, w locie. Czy nie dałoby się tego jakoś scache'ować?
Poręczny cache
Nie musimy za każdym razem generować plików od nowa. Starczy, że zrobimy to raz, zapiszemy i będziemy jedynie odczytywać już istniejący plik.
Oto kod `javascript.php':
<?php
$file_path = 'js/file_min.js';
if ( !file_exists($file_path))
{
require_once 'jsmin.class.php';
$code = JSMin::minify(file_get_contents('js/file_0.js'));
$code .= JSMin::minify(file_get_contents('js/file_1.js'));
$file = fopen($file_path, 'w+');
fwrite($file, $code);
fclose($file);
}
echo file_get_contents($file_path);
Niedosyt...
Ale co to nam dało? Mogliśmy od razu na sztywno skleić te dwa pliki i ładować je jako jeden. Czy nie da się tego jakoś rozwiązać?
... i rozwiązanie
Owszem da. Załóżmy, że będziemy pliki ładować w ten sposób:
<script src="/javascript.php?files=file_0,file_1" type="text/javascript"></script>
Co będzie oznaczać, że chcemy załadowac pliki: `js/file_0.js' oraz `js/file_1.js'. Musimy zatem dokonać drobnej zmiany w naszym skrypcie `javascript.php':
<?php
if (empty($_GET['files'])) // 1
{
die('/* błąd ładowania plików */');
}
define('PATH', 'js/'); // 2
define('EXT', '.js'); // 2
$files = $_GET['files'];
$f_md5 = md5($files); // 3
$file_path = createFilePath($f_md5);
if ( !file_exists($file_path)) // 4
{
require_once 'jsmin.class.php';
$code = includeJSFiles($files); // 5
saveMinimizedCode($file_path, $code); // 6
die($code); // 7
}
echo file_get_contents($file_path); // 7
function includeJSFiles($files)
{
$f_array = explode(',', $files);
$n = count($f_array);
$code = '';
for($i = 0; $i < $n; $i++)
{
$file_path = createFilePath($f_array[$i]);
if (file_exists($file_path))
{
$code .= JSMin::minify(file_get_contents($file_path));
}
}
return $code;
}
function saveMinimizedCode($file_path, $code)
{
$file = fopen($file_path, 'w+');
fwrite($file, $code);
fclose($file);
}
function createFilePath($name, $path = PATH, $ext = EXT)
{
return $path . $name . $ext;
}
- Wstępna walidacja danych wejściowych.
- Stała określająca położenie plików js oraz stała określająca rozszerzenie, jakie chcemy aby pliki z kodem js posiadały.
- Generujemy funkcję skrótu md5, unikając sytuacji w której w nazwie pliku mielibyśmy nieporządane znaki.
- Sprawdzamy, czy istanieje już plik ze scalonym kod js.
- Załączamy wszystkie pliki JS podane w liście przekazanej do skryptu.
- Zapisujemy w pliku `$file_path' wcześniej uzyskany kod [`$code'].
- Wyświetlamy zawartość pliku. W przypadku generowania nowego pliku wyświetlamy zawartość zmiennej $code. W przypadku pliku, który już istniał po prostu wyświetlamy całą jego zawartość.
W folderze `js' powstał plik o nazwie `b9e48609b96e02cef752c067466e7ecb.js' i tenże plik został wysłany do klienta jako odpowiedź na żądanie. Oczywiście stosując tę metodę można spokojnie załączyć więcej plików.
Zadanie domowe
Kod na ostatnim listingu ma kilka wad:
- Słaba walidacja danych wejściowych (nie mówiąc, że brzydka - może regExp?).
- Jest proceduralny. Z pewnością wcześniej, czy później następi konfilkt nazw.
- Posiada stałe, o krotkich nazwach [PATH i EXT], które z pewnością wystapią w każdym innym cudzym kodzie, który będziesz chciał dołączyć do swojej strony. Dobre rozwiązanie pkt 2 naprawia i tę lukę.
- Nie zabezpiecza przed próbą dostępu do jednego pliku przez wielu użytkowników na raz (funkcja `saveMinimizedCode()' - flock).
- Brak możliwości wymuszenia generowania ponownie pliku ze scalonym kodem (np. za pomocą jakiejś flagi przesyłanej w GET)
Zrobiłem to trochę celowo, a trochę z braku czasu. Polecam pobawienie się i załatanie owych "dziur" :). Gdyby ktoś chciał się później podzielić kodem, to zapraszam :)
|