欢迎访问我的新主页:http://huoding.com/
作者:老王
Page Flushing不是啥新概念,几年前雅虎就在
Best Practices for Speeding Up Your Web Site里提过Flush the Buffer Early,可惜我一直没太在意,不过今天在翻看
Velocity 2010 Speaker Slides & Video中的资料时,发现了
Building Performance Into the New Yahoo! Homepage,里面再次强调了这个问题。
Page Flushing的目的在于让让浏览器尽可能快的得到数据,以便渲染,在PHP里有一个flush函数很有用:
<!-- css -->
</head>
<?php flush(); ?>
<body>
<!-- content -->
通过在head后面放置flush,即便后面的内容还没有生成,也可以先让客户端得到头部的内容,从而尽快开始下载css数据(这里我没说js,因为从完美角度看,js应该尽可能放在页面尾部,而不是放在head里),如此一来速度会快一些,从而改善前端的用户体验。
在head后放置flush是最常见的方法,但不便于演示,下面看看在body中使用flush的例子:
<html>
<head>
<title>DEMO</title>
</head>
<body>
<div>hello</div>
<?php ob_flush(); flush(); sleep(1); ?>
<div>world</div>
<?php ob_flush(); flush(); sleep(1); ?>
</body>
</html>
注意:因为有的PHP环境会打开输出缓冲,所以上面使用了ob_flush方法排除这种情况。
使用Firefox浏览上面代码,就能看到效果了,但是如果你使用IE浏览的话,会发现无效,原因在PHP手册中对flush函数的描述里已经说得很清楚了:
Some versions of Microsoft Internet Explorer will only start to display the page after they have received 256 bytes of output, so you may need to send extra whitespace before flushing to get those browsers to display the page.
也就是说 如果内容太短的话,IE会先缓冲内容,到了一定的长度再输出,如果你遇到了这个问题,可以发送空白字符来凑数。
最后再说一个问题:
<html>
<head>
<title>DEMO</title>
</head>
<body>
<table>
<tr><td>
long string...
<?php ob_flush(); flush(); sleep(1); ?>
</td></tr>
<tr><td>
long string...
<?php ob_flush(); flush(); sleep(1); ?>
</td></tr>
</table>
</body>
</html>
这段代码和上面的代码相比,多了一个页面级的table包裹,如果这样设计话的,即便使用了flush,但由于table标签标签未闭合,所以浏览器不会立即开始渲染,必须等到table标签闭合才可以,如此一来,flush就无意义了,不过经过测试,我发现虽然IE有这个问题,但Firefox似乎不受影响,可不管怎么说,在使用Page Flushing技术的时候,还是应该尽可能避免页面级别的元素包裹,把内容打散,多多使用flush。
补充:flush仅在php作为apache模块安装时才有效,如在ngxin下以fastcgi方式安装php,那么当运行flush的时候,缺省无效果,nginx通过
fastcgi_buffers自己控制输出,按照官方文档描述,可以通过设置
fastcgi_max_temp_file_size为0来关闭fastcgi_buffers,但经过测试发现还是不能达到预期效果,如果想达到类似的效果,必须设置一个较小的fastcgi_buffer_size,并且关闭gzip。
补充:很多模板引擎有block功能,可以在block开始和结束的时候加上钩子,整合这个功能。
链接:
深入理解 ob_flush和flush的区别
PHP实时输出报文到浏览器