服务php为css / js:它足够快吗?有什么缺点?

时间:2011-04-02 08:17:29

标签: php javascript css optimization performance

我最近开始进入优化客户端的性能和加载时间,压缩css / js,gzipping,关注YSlow等领域。

我想知道,在尝试实现所有这些微优化时,将php文件作为css或javascript提供的优缺点是什么?

我不完全确定瓶颈在哪里,如果有的话。我认为在相同的CSS和php文件之间,“纯”的css文件会稍微快一点,因为它不需要解析php代码。但是,在php文件中,您可以更好地控制标题,这可能更重要(?)。

目前我正在对“触发器”文件进行filemtime()检查,并使用一些php voodoo从中编写单个压缩css文件,并与定义组中的其他几个文件结合使用。这将创建一个类似css/groupname/301469778.css的文件,php模板捕获并使用新文件名更新html标记。它似乎是最安全的方法,但我不喜欢服务器缓存在几次编辑后被垃圾css文件填满。对于仅为某些页面加载的小“帮助”css文件,我也不打算这样做。

  • 如果99%的输出是由php生成的,那么使用php直接输出css / js内容有什么害处(如果有的话)? (假设没有php错误)
  • 如果使用php,mod_rewrite使用css / js扩展名的文件是否适用于浏览器误解的任何边缘情况?不能受伤?不需要吗?
  • css和javascript是否有单独的指南/方法?我认为他们是平等的。
  • 哪个更快:一个包含多个@imports的css文件,或一个包含多个readfile()来电的php文件?
  • 使用php的其他方式会影响速度吗?
  • 文件在浏览器中缓存后,是否会产生影响?

我更喜欢使用带有.htaccess的php,因为它更简单,但最后我会使用最好的方法。

5 个答案:

答案 0 :(得分:4)

我们正在使用大约2 MB以上的JavaScript代码开发非常大的DHTML / AJAX Web应用程序,并且仍然可以通过一些优化快速加载:

  • 尝试减少包含的脚本网址的数量。我们使用一个简单的PHP脚本来加载一堆.js文件并将它们一次性发送到浏览器(所有连接)。当您拥有大量.js文件时,这将使您的页面加载 lot 更快,因为设置HTTP连接的开销通常远高于实际传输内容本身。请注意,浏览器需要同时下载JS文件。

  • 缓存友好。我们的HTML页面也是通过PHP生成的,脚本的URL包含一个依赖于文件修改时间的哈希。上面结合.js文件的PHP脚本然后检查HTTP缓存标头并设置一个很长的过期时间,这样浏览器甚至不必在用户第二次访问页面时加载任何外部脚本。

  • GZIP压缩脚本。这将使您的代码减少约90%。我们甚至不需要缩小代码(这使调试更容易)。

所以,是的,使用PHP发送CSS / JS文件可以大大缩短页面的加载时间 - 特别是对于大页面。

编辑:您可以使用此代码组合文件:

function combine_files($list, $mime) {

  if (!is_array($list))
    throw new Exception("Invalid list parameter");

  ob_start();

  $lastmod = filemtime(__FILE__);

  foreach ($list as $fname) {
    $fm = @filemtime($fname);

    if ($fm === false) {
      $msg = $_SERVER["SCRIPT_NAME"].": Failed to load file '$fname'";
      if ($mime == "application/x-javascript") {
        echo 'alert("'.addcslashes($msg, "\0..\37\"\\").'");';
        exit(1);
      } else {
        die("*** ERROR: $msg");
      }        
    }

    if ($fm > $lastmod)
      $lastmod = $fm;
  }

  //--

  $if_modified_since = preg_replace('/;.*$/', '', 
    $_SERVER["HTTP_IF_MODIFIED_SINCE"]);


  $gmdate_mod = gmdate('D, d M Y H:i:s', $lastmod) . ' GMT';
  $etag = '"'.md5($gmdate_mod).'"';

  if (headers_sent())
    die("ABORTING - headers already sent");

    if (($if_modified_since == $gmdate_mod) or 
    ($etag == $_SERVER["HTTP_IF_NONE_MATCH"])) {
        if (php_sapi_name()=='CGI') {
        Header("Status: 304 Not Modified");
      } else {
        Header("HTTP/1.0 304 Not Modified");
      }
    exit();
    }
    header("Last-Modified: $gmdate_mod");
    header("ETag: $etag");

    fc_enable_gzip();

    // Cache-Control
    $maxage = 30*24*60*60;   // 30 Tage (Versions-Unterstützung im HTML Code!)

    $expire = gmdate('D, d M Y H:i:s', time() + $maxage) . ' GMT';
    header("Expires: $expire");
    header("Cache-Control: max-age=$maxage, must-revalidate");

  header("Content-Type: $mime");

  echo "/* ".date("r")." */\n";
  foreach ($list as $fname) {
    echo "\n\n/***** $fname *****/\n\n";
    readfile($fname);
  }
}


function files_hash($list, $basedir="") {
  $temp = array();
  $incomplete = false;

  if (!is_array($list))
    $list = array($list);

  if ($basedir!="")
    $basedir="$basedir/";

  foreach ($list as $fname) {
    $t = @filemtime($basedir.$fname);
    if ($t===false)
      $incomplete = true;
    else
      $temp[] = $t;
  }

  if (!count($temp))
    return "ERROR";

  return md5(implode(",",$temp)) . ($incomplete ? "-INCOMPLETE" : "");
}


function fc_compress_output_gzip($output) {
  $compressed = gzencode($output);

  $olen = strlen($output);
  $clen = strlen($compressed);

  if ($olen)
    header("X-Compression-Info: original $olen bytes, gzipped $clen bytes ".
      '('.round(100/$olen*$clen).'%)'); 

  return $compressed;
}
function fc_compress_output_deflate($output) {

  $compressed = gzdeflate($output, 9);

  $olen = strlen($output);
  $clen = strlen($compressed);

  if ($olen)
    header("X-Compression-Info: original $olen bytes, deflated $clen bytes ".
      '('.round(100/$olen*$clen).'%)'); 

  return $compressed;

}

function fc_enable_gzip() {
  if(isset($_SERVER['HTTP_ACCEPT_ENCODING']))
    $AE = $_SERVER['HTTP_ACCEPT_ENCODING'];
   else
    $AE = $_SERVER['HTTP_TE'];
  $support_gzip = !(strpos($AE, 'gzip')===FALSE);
  $support_deflate = !(strpos($AE, 'deflate')===FALSE);
  if($support_gzip && $support_deflate) {
    $support_deflate = $PREFER_DEFLATE;
  }
  if ($support_deflate) {
      header("Content-Encoding: deflate");
      ob_start("fc_compress_output_deflate");
  } else{
    if($support_gzip){
      header("Content-Encoding: gzip");
      ob_start("fc_compress_output_gzip");
    } else{
      ob_start();
    }
  }
}

使用files_hash()生成一个唯一的哈希字符串,每当源文件发生更改时都会更改,而combine_files()会将组合文件发送到浏览器。因此,在生成标记的HTML代码时使用files_hash(),并在通过该标记加载的PHP脚本中使用combine_files()。只需将哈希值放在URL的查询字符串中即可。

<script language="JavaScript" src="get_the_code.php?hash=<?=files_hash($list_of_js_files)?>"></script>

确保在两种情况下都指定相同的$ list。

答案 1 :(得分:4)

好的,所以这是你的直接答案:

  • 只要你的代码没问题就没有任何伤害。浏览器不会发现任何差异。
  • 不需要mod_rewrite。浏览器通常不关心URL(通常甚至不关心MIME类型)。
  • CSS文件通常较小,通常一个文件就足够了,因此无需合并。请注意,组合来自不同目录的文件会影响CSS中引用的图像,因为它们保持相对于CSS URL
  • 肯定readfile()会更快,因为@import需要多个HTTP请求并且你想尽可能地减少
  • 在比较单个HTTP请求时,PHP可能会稍慢。但是,除非你离线执行,否则你无法合并文件。
  • 不,但浏览器缓存不可靠,不正确的Web服务器配置可能会导致浏览器不必要地重新获取URL。

不可能给你一个更具体的答案,因为它取决于很多你的项目细节。

答案 2 :(得分:1)

你所说的是通过PHP提供静态文件,实际上没有什么意义,因为它总是比服务普通文件的Apache慢。一个CSS @import会比PHP的readfile()更快,但是通过提供一个缩小的CSS文件可以获得最佳性能,该文件结合了你需要使用的所有CSS。

如果听起来像你在正确的轨道上。我建议预处理你的CSS并保存到磁盘。如果您需要为缓存之类的内容设置特殊标头,请在VirtualHost指令或.htaccess文件中执行此操作。

为避免大量缓存文件,您可以为缩小的CSS使用简单的文件命名约定。例如,如果您的主CSS文件名为main.css并且它通过@imports引用reset.css和forms.css,则缩小版本可以称为main.min.css

重新生成此文件时,它只是替换它。如果在HTML中包含对该文件的引用,则可以将该请求发送给PHP(如果该文件不存在),合并并缩小文件(通过YUI Compressor之类的内容),并将其保存到磁盘,因此通过普通HTTP为所有未来请求提供服务。

更新CSS时,只需删除main.min.css版本,它就会自动重新生成。

答案 3 :(得分:0)

您可以使用ANT Build进行预处理。对不起,这篇文章是德语,但我已经尝试过translate.google.com并且工作正常:-)所以你可以使用帖子作为教程来获得更好的表现...... 我会预处理文件并将它们保存到磁盘,就像simonrjones说的那样。缓存等等应该由专用元素完成,如Apache WebServer,Headers和Browser。

答案 4 :(得分:0)

虽然速度较慢,但​​您可能必须执行此操作的一个优点/原因是将动态内容放入服务器上的文件中,但仍然从客户端角度看它们是js或css。

就像这样,例如,将环境从php传递给javascript:

var environment = <?=getenv('APPLICATION_ENV');?>

// More JS code here ...