Blog webdeveloperski Patryk yarpo Jar

Zmniejszenie liczby requestów dla plików JS

Autor wiadomości Kwiecień 15, 2011

Niedawno pokazałem, jak z wykorzystaniem JSMina sprawić, aby pobierane pliki JS były mniejsze. W tym wpisie pokażę, co zrobić, aby ograniczyć liczbę requestów HTTP przy ładowaniu plików JS.

Powolne ładowanie strony

Aby strona ładowała się szybciej, można zrobić dwie rzeczy:

  1. Zmniejszyć jej rozmiar - im mniej KB do pobrania, tym szybciej się to stanie.
  2. Zmniejszyć liczbę odwołań do serwera. Jedno odwołanie żądające 100KB będzie działać szybciej niż 10 żądań po pliki 10KB każdy.

W przypadku JS punkt pierwszy można rozwiązać albo pisząc krótsze skrypty (lub rezygnując z nich), albo zmniejszając rozmiar kodu wynikowego.

Zmniejszanie liczby requestów HTTP

Idea jest dosyć prosta. Jeśli chcemy załączyć pliki JS zawsze robiliśmy tak:

<html>
<head>
    <script src="js/file_1.js" type="text/javascript"></script>
    <script src="js/file_2.js" type="text/javascript"></script>
    <!-- dowolna liczba kolejnych plików -->
</head><body></body></html>

A co, gdybyśmy zrobi tak:

<html>
<head>
	<script src="js/file_1.js,js/file_2.js" type="text/javascript"></script>
</head><body></body>

Czyż nie lepiej? Mniej kodu, a i powinien to być jeden request. O to chodziło! Ale zaraz, zaraz... To nie działa 🙁

Wykorzystanie skryptu PHP

Przykład z poprzedniego listingu nie był tak znowu totalnie skopany. Da się to zrobić w podobny sposób.

Zróbmy najpierw plik 'script. php':

<?php echo 'alert("Działa!")'; ?>

I zmieńmy lekko kod HTML:

<html>
<head>
    <script src="script.php" type="text/javascript"></script>
</head>
<body></body></html>

To działa! Pójdź zatem krok dalej.

Przekazywanie zmiennych do skryptu

Przekażmy do skryptu parametry:

<html>
<head>
    <script src="script.php?param=ok" type="text/javascript"></script>
</head>
<body></body></html>

Odbieranie parametrów w skrypcie

W skrypcie PHP możemy odebrać te parametry za pomocą tablicy superglobalnej `$_GET'.

<?php echo 'alert("Zmienna: ' . $_GET['param'] . '")';

W wyniku zostanie wyświetlone "zmienna: ok".

Pliki

Skoro można przekazać do skryptu dane, a skrypt może nam zwrócić kod JS, to dlaczego nie wykorzystać tego do naszego celu - za pomocą jednego requesta HTTP odczytać więcej niż jeden plik JS.

Kod HTML:

<html>
<head>
    <script src="script.php?files=file_1,file_2" type="text/javascript"></script>
</head>
<body></body></html>

Oraz kod skryptu PHP:

<?php
$files = explode(',',  $_GET['files']);
$n = count($files);
$code = '';
for($i = 0; $i < $n; $i++)
{
    $code .= file_get_contents($files[$i] . '.js');
}
echo $code;

Dla prostoty kodu zrezygnowałem z jakiejkolwiek walidacji.

Takie rozwiązanie rzeczywiście pozwala na zmniejszenie liczby requestów. Niestety odczytywanie za każdym razem przy odświeżeniu strony kilkudziesięciu plików JS może sprawić, że nasza strona nie przyspieszy za wiele. Być może nawet zwolni. Dodatkowo serwer będzie miał dużo pracy... Spróbujmy coś z tym zrobić.

Kompresja kodu + cache

Dlaczego nie użyć metody opisanej we wpisie o JSMin? Dodać do tego jedynie pętlę, którą pokazałem wyżej.

Dodatkowo warto stworzyć cache na już raz skomponowane pliki. Jak je nazwać? Proponuję funkcję skrótu md5.

W zmiennej `$_GET['files']' jest jakas wartość. Nie wiadomo, jak długa. Nie wiadomo z jakimi znakami. Warto zatem zapisac plik o nazwie:

md5($_GET['files']);

Oto moje rozwiązanie:

<?php
if (empty($_GET['files']))
{
    die('/* błąd ładowania plików */');
}

require_once 'js/jsmin.php';
require_once 'js/jscacher.php';

$cacher = new JSCacher();
//$cacher->extension('.js');
//$cacher->minPath('js/min');
//$cacher->path('js/full');
//$cacher->separator(',');
die($cacher->minimize($_GET['files']));

jak widać klasa ta ma przejrzysty i prosty interfejs. Zakomentowałem linie ustawiające pewne opcje (ścieżki do katalogów oraz separator oddzielający w parametrze nazwy plików]).

Kod klasy JSCacher

najnowsza wersja kodu zawsze dostępna na svn:

class JSCacher
{
	private $sep = ',';
	private $ext = '.js';
	private $path = 'js/full/';
	private $minPath = 'js/min/';
	private $minFile;

	public function minimize($files, $recache = false)
	{
		$this->minFile = $this->createFilePath(md5($files), $this->minPath);

		if (true === $recache || !file_exists($this->minFile))
		{
			return $this->readAndShrink($files);
		}
		else if (file_exists($this->minFile))
		{
			return file_get_contents($this->minFile);
		}
	}

	private function readAndShrink($files)
	{
		$filesList = explode($this->sep, $files);
		$n = count($filesList);
		$code = '';

		for($i = 0; $i < $n; $i++)
		{
			$filePath = $this->createFilePath($filesList[$i], $this->path);
			if (file_exists($filePath))
			{
				$code .= JSMin::minify(file_get_contents($filePath));
			}
		}

		file_put_contents($this->minFile, $code);
		return $code;
	}

	private function createFilePath($file, $path)
	{
		return $path . trim($file) . $this->ext;
	}

	public function extension($v = false)
	{
		if (false !== $v)
		{
			$this->ext = $v;
		}
		return $this->ext;
	}

	public function path($v = false)
	{
		if (false !== $v)
		{
			$this->path = $v;
		}
		return $this->path;
	}

	public function minPath($v = false)
	{
		if (false !== $v)
		{
			$this->minPath = $v;
		}
		return $this->minPath;
	}

	public function separator($v = false)
	{
		if (false !== $v)
		{
			$this->sep = $v;
		}
		return $this->sep;
	}
}

Aby wszystko działało jak należy warto posiadać podobną strukturę:

/public_html
    index.html
    script.php
    js/
        cacher.php
        jsmin.php
        full/ <- tu oryginalne pliki JS
        min/  <- tu zmniejszone pliki JS

Do pobrania:

Warto przeczytać:

Komentarze (0) Trackbacks (0)

Brak komentarzy.


Leave a comment

 

Brak trackbacków.