将 php 作为 css/js 提供服务:速度够快吗?有啥缺点?

Posted

技术标签:

【中文标题】将 php 作为 css/js 提供服务:速度够快吗?有啥缺点?【英文标题】:Serving php as css/js: Is it fast enough? What drawbacks are there?将 php 作为 css/js 提供服务:速度够快吗?有什么缺点? 【发布时间】:2011-07-28 04:22:09 【问题描述】:

我最近开始涉足优化性能和加载时间客户端、压缩 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 的其他哪些方式会影响速度? 文件在浏览器中缓存后,是否有任何不同?

我更喜欢将 php 与 .htaccess 一起使用,因为它更简单,但最终我会使用任何最好的方法。

【问题讨论】:

我很好奇为什么您需要在 CSS 或 JavaScript 文件中使用通用标题。我很少看到 CSS/JS 文件在提供给客户端时需要修改。我看到的大多数 PHP 用于 HTML 文件,即使是 JavaScript 代码使用的某些 id 或配置变量,通常也插入到 HTML 文件中,而不是接触 JavaScript 文件。 除非你只添加头文件,否则处理 JS 文件会阻止你使用压缩器、优化器和编译器来缩小或优化它们。对于 CSS,根据您需要添加的动态元素(这就是您需要 PHP 的原因),您可能还会阻止压缩 CSS 文件。您正在失去的不仅仅是使用 PHP 的开销。 @Stephen Chung:将js/css直接放在html源中意味着不能与html文档分开缓存。我还想知道是否使用 php 通过将其“包装”在 php 中来设置诸如 expires 和 last mod time 之类的标头是一个好主意。我不清楚您所说的“处理文件会阻止您缩小它们”是什么意思。如果我将文件的输出发送到压缩器,它应该不会有什么不同。 @Madmartigan,我不建议将 CSS/JS 合并到您的 HTML 中。我只是说你确实没有必要将 PHP 与 CSS/JS 文件一起使用。如果您正在考虑为它们设置到期日期等,应该有更简单的方法在您的网络服务器中执行此操作。 @Stephen Chung:虽然我同意没有需要,但将 php 用于 css/js 肯定有很多好处。我的问题是是否有任何缺点,它们是什么,以及是否应该避免或仅在特定情况下使用。 【参考方案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 版本,它就会自动重新生成。

【讨论】:

感谢您的反馈。更新文件名本身的原因是为了确保已经拥有 css 文件缓存版本的用户下载更新的文件(如果存在)。由于文件名发生变化,浏览器会将其视为新文件,不会使用缓存版本。 @importreadfile() 快多少,只是好奇? 如果您担心更新,只需在您的 CSS 文件中添加一个虚假的 GET 参数,例如: 这将强制重新加载,您可以保留原始文件名。您必须对 1000 个请求进行一些测试,以查看 @import 与 readfile() 之间的确切时间差,但一般来说,PHP 总是会产生静态文件所没有的开销。而且一个组合的静态文件总是比多个文件或 PHP 快。 与@import/readfile 的不同之处在于,在readfile() 的情况下,客户端只需要下载1 个文件,因此除了初始开销之外,还需要考虑这一点。我也读过很多次,使用查询字符串不够可靠,无法触发下载新文件。需要的可以引用。【参考方案2】:

您可以使用ANT Build 进行预处理。抱歉,这篇文章是德语的,但我试过 translate.google.com 并且效果很好 :-) 所以你可以使用这篇文章作为教程来获得更好的性能...... 我会预处理文件并将它们保存到磁盘,就像 simonrjones 说的那样。缓存等内容应由专用元素完成,如 Apache WebServer、Headers 和 Browser。

【讨论】:

【参考方案3】:

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

尝试减少包含的脚本 URL 数量。我们使用一个简单的 PHP 脚本来加载一堆 .js 文件并将它们一次性发送到浏览器(全部连接)。当您像我们一样拥有大量 .js 文件时,这将使您的页面加载速度很多,因为设置 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。

【讨论】:

你是说没有缺点,只有优点?当你说你使用哈希时,你的意思是像style.css#v1吗?就确保客户端在可用时下载style.css#v2 而言,这是否可靠?正如我所评论的,我读过附加查询字符串是不可靠的,这种方法看起来很相似。另外,我知道减少请求是一件好事,但是使用 php 提供内容时的速度呢?我假设 php 在客户端请求该文件之前不会编译为 css,因为页面正在加载。这不会影响 css/js 的可用性吗? 嗯,这取决于。使用 PHP 提供单个 100 字节的 CSS 文件不会使其更快。但是只用一个 PHP HTTP 请求来提供 20 个繁重的 .js 文件将使您的页面在 1-2 秒内加载,而不是 10 多秒。当然这也取决于你的 PHP 安装,但通常 PHP 代码是用一些加速器预编译的。 我在我的解决方案中添加了代码。我说的不是 URL 哈希,而是 MD5 哈希,即只是一个在源文件的 modtime 更改时更改的字符串。作为查询字符串的一部分,它使 URL 唯一,并确保浏览器加载最新版本的代码。我不明白为什么这不可靠。如果页面加载更快,请尝试使用 FireBug 查看。 感谢您提供代码示例,但我不想专注于“如何”,正如我在原始帖子中所说的那样,我已经把它记下来了。问题是关于使用 php 服务 css/js 的性能。我现在还提到过两次,查询字符串可能不够可靠,无法在修订时强制重新下载(这是题外话)。我想听听关于我问题中要点的一些答案,特别是如果使用 php 有缺点,但还没有人真正触及过。也许答案是“不,没有缺点”。不会更快!== 会更慢 @Madmartigan:请提供关于不可靠查询字符串方法的引用。【参考方案4】:

好的,下面是你的直接答案:

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

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

【讨论】:

+1 有趣的是 readfile() 与 @import.. 这是我所期望的,但 @simonrjones 不同意,你听起来很确定。如果您有任何资源可以分享,我将有兴趣阅读更多有关此内容的信息。我知道我真的很担心,但这就是优化的全部内容。我想跳过某些文件/组的最小/组合步骤,但担心使用 php 为它们提供服务可能会以某种方式产生负面影响。我以前做过很多次,但我不知道每一个边缘情况。感谢您的回复。 在使用 PHP 时我能想到的唯一负面影响是它实际上可能更慢,但这取决于文件的数量、文件的大小,尤其是您的 PHP 安装。澄清的唯一方法是在实践中比较这两种方法。根据您的项目,您还可以选择将脚本预先组合到一个静态文件中(即离线执行)。无论如何,一切都与速度和项目管理有关,您可以自行权衡解决方案。 我想对此添加一个更新:如果您使用的是 HTTP2,那么优先减少 HTTP 请求的数量是没有必要或明智的。 HTTP2 允许请求并行发生,而合并文件有点违背了这样做的目的。【参考方案5】:

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

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

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

// More JS code here ...

【讨论】:

以上是关于将 php 作为 css/js 提供服务:速度够快吗?有啥缺点?的主要内容,如果未能解决你的问题,请参考以下文章

Nginx 为 CSS/JS 文件提供 403 错误

xml,php,css,js,less这些都有啥意思?

一秒下载千部电影不是梦,澳洲新研究公布,WiFi又要被淘汰了

php 将css / js添加到wordpress

如何将html+css+js打包成apk或者ipa文件?

Git