Jag tänkte laborera lite med optimering av javascript och css filer.
Grundidén är att en webbsida har ofta ett antal js och css filer som laddas in vid varje sidanrop. Givetvis cachas dessa hos webbläsaren men man kan få ut lite extra boost genom att packa ihop (ta bort kommentarer osv) och sammanfoga dessa till en enda fil (packad.js, packad.css).
Hur har ni löst detta? Känner ni till bra färdiga script som hanterar detta?
Tanken är att man kan via ett administrativt gränssnitt välja, om man vill optimera eller inte.
För sakens skull kan jag nämna att jag kör med Zend Framework.
Vad jag menar med komprimering i detta fall är att man först tar bort onödiga tecken osv från js och css filerna. T ex kommentarerna är ju oväsentliga för parsern, men de tar plats och bandbredd. Sedan kan man givetvis packa DESSA modifierade filer med vad som nu stöds av systemet (t ex just det som du nämde).
Jag vill alltså:
filA.css
filB.css
filC.css
->
joined.css
filA.js
filB.js
->
joined.js
Så istället för att ha femtioelva css och js includes i html-filen så har jag två, en för css och en för js! :)
Det kan också vara bra att skicka javascript, css och andra statiska filer från en annan domän eller subdomän ifall man använder nån form av cookie-data. Då skickas nämligen inte cookien med hela tiden, vilket kan snabba upp kommunikationen lite.
Det kan också vara bra att skicka javascript, css och andra statiska filer från en annan domän eller subdomän ifall man använder nån form av cookie-data. Då skickas nämligen inte cookien med hela tiden, vilket kan snabba upp kommunikationen lite.
Dessutom kan webbläsarna ladda ner övrigt innehåll parallellt.
Jag har också laborerat lite med detta, och skapat en FileProvider-klass till ett eget ramverk (finns under sidan i min sign.). Det fungerar faktiskt riktigt bra, jag YUI-komprimerar även JavaScript och CSS innan jag sätter projekt i produktion.
Klassen i nästan sin helhet (ett par omskrivningar har jag bara gjort för bättre förståelse);
<?php
/**
* Bw Framework File Provider Class
*
* Bw Framework :: Lightweight MVC Framework, in an easy-to-use suit
* using Smarty as template engine
*
* PHP version 5.3 >
*
* (c) Copyright 2009, Björn Wikström
* (c) Copyright of third party software [Smarty], PHP Group
* (c) Copyright of third party software [FirePHP], Christoph Dam
*
* @category BwFramework
* @package BwFramework
* @author Björn Wikström <bjorn@bwit.se>
* @license http://www.gnu.org/copyleft/lesser.html LGPL, Lesser General Public License
* @version 1.0
* @link http://dev.bwit.se/framework
*/
/**
* BwFramework File Provider Class
*
* This class is used gzipping and caching css and javascript
*
* @category BwFramework
* @package BwFramework
* @author Björn Wikström <bjorn@bwit.se>
* @license http://www.gnu.org/copyleft/lesser.html LGPL, Lesser General Public License
* @version 1.0
* @link http://dev.bwit.se/framework
*/
class FileProvider
{
/**#@+
* @access const
*/
const CSS = 'css';
const JS = 'javascript';
/**#@-*/
/**#@+
* @access private
*/
private $_type;
private $_charset;
private $_extension;
private $_cache;
private $_assets;
/**#@-*/
/**
* This method starts the file provider
*
* @param string $type The type of the files to be provided - css/js
* @param string $files The files to be provided, separated by comma
* @param string $charset The charset to be transferred in
*
* @access public
* @return void
*/
public static function provide($type, $files, $charset = 'UTF-8')
{
$fp = new FileProvider();
$fp->_start($type, str_replace(' ', '', $files), $charset);
}
/**
* Private constructor
*
* @access constructor
*/
private function __construct()
{
}
/**
* Set up file paths and creating cache
*
* @param string $type The type of the file to be provided - css/js
* @param string $files The files to be provided, separated by comma
* @param string $charset The charset to be transferred in
*
* @access private
* @return void
*/
private function _start($type, $files, $charset = 'UTF-8')
{
$this->_cache = '/non-www-accessible-cache-folder/';
$this->_assets = '/javascript-and-css-folder/';
$this->_type = $type;
$this->_charset = $charset;
if ($type == 'css') {
$this->_extension = '.css';
} else {
$this->_extension = '.js';
}
ini_set("zlib.output_compression", "Off");
if ($this->_getLatestModTime(explode(',', $files)) > $this->_getLatestCachingTime($type) || !file_exists($this->_cache . $this->_type)) {
$this->_createCache(explode(',', $files));
}
$this->_output();
}
/**
* Get the latest modification time of the files
*
* @param array $files The files to be provided
*
* @access private
* @return int
*/
private function _getLatestModTime(array $files)
{
$latestModTime = 0;
foreach ($files as $file) {
if (!file_exists($this->_assets . $this->_type . DIRECTORY_SEPARATOR . $file . $this->_extension)) {
$latestModTime = 0;
break;
}
$currentModTime = @filemtime($this->_assets . $this->_type . DIRECTORY_SEPARATOR . $file . $this->_extension);
if ($currentModTime > $latestModTime) {
$latestModTime = $currentModTime;
}
}
return $latestModTime;
}
/**
* Get the latest modification time of the cache
*
* @param string $type The type of the files to be provided
*
* @access private
* @return int
*/
private function _getLatestCachingTime($type)
{
return @filemtime($this->_cache . $type);
}
/**
* Create a cache of the files and their contents
*
* @param array $files The files to be provided
*
* @access private
* @return void
*/
private function _createCache(array $files)
{
ob_start();
foreach ($files as $file) {
if (!file_exists($this->_assets . $this->_type . DIRECTORY_SEPARATOR . $file . $this->_extension)) {
continue;
}
readfile($this->_assets . $this->_type . DIRECTORY_SEPARATOR . $file . $this->_extension);
}
$content = ob_get_contents();
ob_end_clean();
$pointer = @fopen($this->_cache . $this->_type, 'w');
@fwrite($pointer, $content);
@fclose($pointer);
$pointer = @fopen($this->_cache . $this->_type . '.gz', 'w');
@fwrite($pointer, gzencode($content, 9, FORCE_GZIP));
@fclose($pointer);
}
/**
* Output the merged files to the client, with gzip compression if
* the client can decompress them - plain text otherwise
*
* @access private
* @return void
*/
private function _output()
{
header("Content-Type: text/" . $this->_type . "; charset=" . $this->_charset);
header("Cache-Control: must-revalidate");
header("Expires: " . gmdate('D, d M Y H:i:s', time() + (3600 * 12)) . " GMT");
if (strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) {
header("Content-Encoding: gzip");
header("Content-Length: " . filesize($this->_cache . $this->_type . '.gz'));
readfile($this->_cache . $this->_type . '.gz');
} else {
header("Content-Length: " . filesize($this->_cache . $this->_type));
readfile($this->_cache . $this->_type);
}
die();
}
}
Jag orkade inte klippa ned det till det mest vitala, men undrar du något ska jag försöka ta ork till mig och svara. ;)
Jag hade inte sett Googles Closure Compiler, trevligt. Dock verkar den inte vara något "jätteextra" om man jämför med YUI - jag tjänade ingenting med SIMPLE_OPT. Med ADVANCED_OPT tjänade jag dock mellan 1-2 kb på ett mindre JS-dokument (~10kb), dock fick jag varningar då.
138 ms totalt · 3 externa anrop · v20260731065814-full.25f56b17