【问题标题】:Flushing the buffer after rendering the head of a page in Zend Framework在 Zend Framework 中渲染页面头部后刷新缓冲区
【发布时间】:2015-01-14 02:31:19
【问题描述】:

我们的应用程序在 head 标签中有很多 CSS 和 JS,为了提高感知页面加载速度,我希望页面尽快开始获取外部内容,即使在等待服务器提供其他内容。基本上我的目标是让浏览器在等待正文内容交付时开始获取 head 标签中的 CSS 和 JS。

所以在主布局页面中,我尝试在渲染头部标签后刷新缓冲区(在 php.ini 中关闭了 output_buffering):

<?php ob_start(); ?>
<head>
<?php
// A bunch of function calls to render tags for external style sheets and JS
?>
</head>
<?php ob_end_flush(); ?>
<body>
Body content rendered here . . .
</body>

这基本上没有效果。当我查看 Chrome 中的网络活动时,我看到 head 标签和 body 标签都在一个响应中交付,只有在完成加载之后,其他资源才开始按顺序加载,而不是像我预期的那样并行加载。

我尝试了许多其他组合,但运气不佳。我还读到刷新输出缓冲区后需要调用 flush() 但这给了我奇怪的结果:页面只显示了一堆奇怪的外来字符(也许它试图显示二进制文件或其他东西?) .

我什至尝试只在 head 和 body 标签之间调用 flush() ,但这引发了一个错误,告诉我我正在尝试修改已经发送的 headers,所以没有做任何事情对我也有用。

然后我尝试查看是否存在特定于 Zend Framework 的解决方案。有人告诉我我正在尝试做的事情是不可能的,因为 ZF 从内存中的模板渲染整个页面,因此它不能在渲染过程中被刷新并发送回浏览器,而 ZF 基本上忽略 PHP 的 output_buffering 配置值。

我想我已经用尽了所有可能的组合刷新功能,所以我想知道是否有其他人遇到过类似的问题以及方法是什么。

服务器版本:Apache/2.2.22 (Ubuntu)

(顺便说一句,是的,我知道应该在页面底部加载 JS,但这不是我将其放入 head 标签中的要求)

更新 1

当我尝试以下操作时,我得到了我想要的,部分

<?php ob_start('ob_gzhandler'); ?>
<head>
<?php
// A bunch of function calls to render tags for external style sheets and JS
?>
</head>
<?php ob_end_flush(); ?>
<body>
Body content rendered here . . .
</body>

我为 ob_start 添加了一个回调到 ob_gzhandler。这样做会返回一个包含 head 标签的响应,没有正文。这很好,因为一旦 head 标签到来,资源就会开始加载,但 body 标签永远不会到来,页面基本上是空白的。就好像告诉它刷新刷新缓冲区,但是脚本在完成执行后再也不会刷新。

我已经在布局脚本的末尾明确添加了对 ob_flush(和其他变体)的调用,但它们要么不做任何事情,要么抱怨没有要刷新的缓冲区(可能是因为我之前调用了 ob_end)。

【问题讨论】:

    标签: php zend-framework flush output-buffering


    【解决方案1】:

    从未来从事遗留 ZF1 项目:

    这不起作用的原因仅仅是 ZF 已经在缓冲布局的输出。

    https://github.com/zendframework/zf1/blob/136735e776f520b081cd374012852cb88cef9a88/library/Zend/Layout/Controller/Plugin/Layout.php#L108

    $obStartLevel = ob_get_level();
    try {
        $fullContent = $layout->render();
        $response->setBody($fullContent);
    } catch (Exception $e) {
        while (ob_get_level() > $obStartLevel) {
            $fullContent .= ob_get_clean();
        }
        $request->setParam('layoutFullContent', $fullContent);
        $request->setParam('layoutContent', $layout->content);
        $response->setBody(null);
        throw $e;
    }
    

    因此,视图渲染中的任何其他类型的缓冲区操作只会被刷新到其他缓冲区。而且您也不能终止更高级别的缓冲区,因为setBody 不会捕获输出

    没有合适的解决方案。因此,hackaround是:

    https://gist.github.com/darylteo/0fb8cc59dbcc62c90d1b81502408b7c4

    这是实验性的,我不确定会产生什么影响。


    不知道 ZF2 的解决方案是什么,但希望这对将来的一些可怜的人有所帮助。

    【讨论】:

    • 经过一些测试,很明显这没有任何普遍的好处。这些仅在所有控制器工作完成后才会触发(因此,发送后)。因此,下一个潜在的解决方案是在完成任何有意义的工作之前,实际使用 renderScript() 来渲染动作中的头部。请确保仅在发送所有标头后才执行此操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-12
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多